opencode-swarm 6.43.1 → 6.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +748 -346
- package/dist/config/constants.d.ts +13 -0
- package/dist/config/schema.d.ts +2 -0
- package/dist/hooks/curator-types.d.ts +1 -0
- package/dist/hooks/curator.d.ts +2 -2
- package/dist/index.js +1694 -1131
- package/dist/plan/checkpoint.d.ts +25 -0
- package/dist/plan/checkpoint.test.d.ts +1 -0
- package/dist/plan/ledger-integrity.test.d.ts +5 -0
- package/dist/plan/ledger-snapshot-adversarial.test.d.ts +1 -0
- package/dist/plan/ledger.d.ts +196 -0
- package/dist/plan/ledger.test.d.ts +1 -0
- package/dist/plan/manager.d.ts +9 -0
- package/dist/plan/manager.ledger-aware.test.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -16125,9 +16125,278 @@ var require_proper_lockfile = __commonJS((exports, module) => {
|
|
|
16125
16125
|
module.exports.checkSync = checkSync;
|
|
16126
16126
|
});
|
|
16127
16127
|
|
|
16128
|
+
// src/plan/ledger.ts
|
|
16129
|
+
import * as crypto2 from "crypto";
|
|
16130
|
+
import * as fs4 from "fs";
|
|
16131
|
+
import * as path7 from "path";
|
|
16132
|
+
function getLedgerPath(directory) {
|
|
16133
|
+
return path7.join(directory, ".swarm", LEDGER_FILENAME);
|
|
16134
|
+
}
|
|
16135
|
+
function getPlanJsonPath(directory) {
|
|
16136
|
+
return path7.join(directory, ".swarm", PLAN_JSON_FILENAME);
|
|
16137
|
+
}
|
|
16138
|
+
function computePlanHash(plan) {
|
|
16139
|
+
const normalized = {
|
|
16140
|
+
schema_version: plan.schema_version,
|
|
16141
|
+
title: plan.title,
|
|
16142
|
+
swarm: plan.swarm,
|
|
16143
|
+
current_phase: plan.current_phase,
|
|
16144
|
+
migration_status: plan.migration_status,
|
|
16145
|
+
phases: plan.phases.map((phase) => ({
|
|
16146
|
+
id: phase.id,
|
|
16147
|
+
name: phase.name,
|
|
16148
|
+
status: phase.status,
|
|
16149
|
+
required_agents: phase.required_agents ? [...phase.required_agents].sort() : undefined,
|
|
16150
|
+
tasks: phase.tasks.map((task) => ({
|
|
16151
|
+
id: task.id,
|
|
16152
|
+
phase: task.phase,
|
|
16153
|
+
status: task.status,
|
|
16154
|
+
size: task.size,
|
|
16155
|
+
description: task.description,
|
|
16156
|
+
depends: [...task.depends].sort(),
|
|
16157
|
+
acceptance: task.acceptance,
|
|
16158
|
+
files_touched: [...task.files_touched].sort(),
|
|
16159
|
+
evidence_path: task.evidence_path,
|
|
16160
|
+
blocked_reason: task.blocked_reason
|
|
16161
|
+
}))
|
|
16162
|
+
}))
|
|
16163
|
+
};
|
|
16164
|
+
const jsonString = JSON.stringify(normalized);
|
|
16165
|
+
return crypto2.createHash("sha256").update(jsonString, "utf8").digest("hex");
|
|
16166
|
+
}
|
|
16167
|
+
function computeCurrentPlanHash(directory) {
|
|
16168
|
+
const planPath = getPlanJsonPath(directory);
|
|
16169
|
+
try {
|
|
16170
|
+
const content = fs4.readFileSync(planPath, "utf8");
|
|
16171
|
+
const plan = JSON.parse(content);
|
|
16172
|
+
return computePlanHash(plan);
|
|
16173
|
+
} catch {
|
|
16174
|
+
return "";
|
|
16175
|
+
}
|
|
16176
|
+
}
|
|
16177
|
+
async function ledgerExists(directory) {
|
|
16178
|
+
const ledgerPath = getLedgerPath(directory);
|
|
16179
|
+
return fs4.existsSync(ledgerPath);
|
|
16180
|
+
}
|
|
16181
|
+
async function getLatestLedgerSeq(directory) {
|
|
16182
|
+
const ledgerPath = getLedgerPath(directory);
|
|
16183
|
+
if (!fs4.existsSync(ledgerPath)) {
|
|
16184
|
+
return 0;
|
|
16185
|
+
}
|
|
16186
|
+
try {
|
|
16187
|
+
const content = fs4.readFileSync(ledgerPath, "utf8");
|
|
16188
|
+
const lines = content.trim().split(`
|
|
16189
|
+
`).filter((line) => line.trim() !== "");
|
|
16190
|
+
if (lines.length === 0) {
|
|
16191
|
+
return 0;
|
|
16192
|
+
}
|
|
16193
|
+
let maxSeq = 0;
|
|
16194
|
+
for (const line of lines) {
|
|
16195
|
+
try {
|
|
16196
|
+
const event = JSON.parse(line);
|
|
16197
|
+
if (event.seq > maxSeq) {
|
|
16198
|
+
maxSeq = event.seq;
|
|
16199
|
+
}
|
|
16200
|
+
} catch {}
|
|
16201
|
+
}
|
|
16202
|
+
return maxSeq;
|
|
16203
|
+
} catch {
|
|
16204
|
+
return 0;
|
|
16205
|
+
}
|
|
16206
|
+
}
|
|
16207
|
+
async function readLedgerEvents(directory) {
|
|
16208
|
+
const ledgerPath = getLedgerPath(directory);
|
|
16209
|
+
if (!fs4.existsSync(ledgerPath)) {
|
|
16210
|
+
return [];
|
|
16211
|
+
}
|
|
16212
|
+
try {
|
|
16213
|
+
const content = fs4.readFileSync(ledgerPath, "utf8");
|
|
16214
|
+
const lines = content.trim().split(`
|
|
16215
|
+
`).filter((line) => line.trim() !== "");
|
|
16216
|
+
const events = [];
|
|
16217
|
+
for (const line of lines) {
|
|
16218
|
+
try {
|
|
16219
|
+
const event = JSON.parse(line);
|
|
16220
|
+
events.push(event);
|
|
16221
|
+
} catch {}
|
|
16222
|
+
}
|
|
16223
|
+
events.sort((a, b) => a.seq - b.seq);
|
|
16224
|
+
return events;
|
|
16225
|
+
} catch {
|
|
16226
|
+
return [];
|
|
16227
|
+
}
|
|
16228
|
+
}
|
|
16229
|
+
async function initLedger(directory, planId) {
|
|
16230
|
+
const ledgerPath = getLedgerPath(directory);
|
|
16231
|
+
const planJsonPath = getPlanJsonPath(directory);
|
|
16232
|
+
if (fs4.existsSync(ledgerPath)) {
|
|
16233
|
+
throw new Error("Ledger already initialized. Use appendLedgerEvent to add events.");
|
|
16234
|
+
}
|
|
16235
|
+
let planHashAfter = "";
|
|
16236
|
+
try {
|
|
16237
|
+
if (fs4.existsSync(planJsonPath)) {
|
|
16238
|
+
const content = fs4.readFileSync(planJsonPath, "utf8");
|
|
16239
|
+
const plan = JSON.parse(content);
|
|
16240
|
+
planHashAfter = computePlanHash(plan);
|
|
16241
|
+
}
|
|
16242
|
+
} catch {}
|
|
16243
|
+
const event = {
|
|
16244
|
+
seq: 1,
|
|
16245
|
+
timestamp: new Date().toISOString(),
|
|
16246
|
+
plan_id: planId,
|
|
16247
|
+
event_type: "plan_created",
|
|
16248
|
+
source: "initLedger",
|
|
16249
|
+
plan_hash_before: "",
|
|
16250
|
+
plan_hash_after: planHashAfter,
|
|
16251
|
+
schema_version: LEDGER_SCHEMA_VERSION
|
|
16252
|
+
};
|
|
16253
|
+
fs4.mkdirSync(path7.join(directory, ".swarm"), { recursive: true });
|
|
16254
|
+
const tempPath = `${ledgerPath}.tmp`;
|
|
16255
|
+
const line = `${JSON.stringify(event)}
|
|
16256
|
+
`;
|
|
16257
|
+
fs4.writeFileSync(tempPath, line, "utf8");
|
|
16258
|
+
fs4.renameSync(tempPath, ledgerPath);
|
|
16259
|
+
}
|
|
16260
|
+
async function appendLedgerEvent(directory, eventInput, options) {
|
|
16261
|
+
const ledgerPath = getLedgerPath(directory);
|
|
16262
|
+
const latestSeq = await getLatestLedgerSeq(directory);
|
|
16263
|
+
const nextSeq = latestSeq + 1;
|
|
16264
|
+
const planHashBefore = computeCurrentPlanHash(directory);
|
|
16265
|
+
if (options?.expectedSeq !== undefined && options.expectedSeq !== latestSeq) {
|
|
16266
|
+
throw new LedgerStaleWriterError(`Stale writer: expected seq ${options.expectedSeq} but found ${latestSeq}`);
|
|
16267
|
+
}
|
|
16268
|
+
if (options?.expectedHash !== undefined && options.expectedHash !== planHashBefore) {
|
|
16269
|
+
throw new LedgerStaleWriterError(`Stale writer: expected hash ${options.expectedHash} but found ${planHashBefore}`);
|
|
16270
|
+
}
|
|
16271
|
+
const planHashAfter = options?.planHashAfter ?? planHashBefore;
|
|
16272
|
+
const event = {
|
|
16273
|
+
...eventInput,
|
|
16274
|
+
seq: nextSeq,
|
|
16275
|
+
timestamp: new Date().toISOString(),
|
|
16276
|
+
plan_hash_before: planHashBefore,
|
|
16277
|
+
plan_hash_after: planHashAfter,
|
|
16278
|
+
schema_version: LEDGER_SCHEMA_VERSION
|
|
16279
|
+
};
|
|
16280
|
+
fs4.mkdirSync(path7.join(directory, ".swarm"), { recursive: true });
|
|
16281
|
+
const tempPath = `${ledgerPath}.tmp`;
|
|
16282
|
+
const line = `${JSON.stringify(event)}
|
|
16283
|
+
`;
|
|
16284
|
+
if (fs4.existsSync(ledgerPath)) {
|
|
16285
|
+
const existingContent = fs4.readFileSync(ledgerPath, "utf8");
|
|
16286
|
+
fs4.writeFileSync(tempPath, existingContent + line, "utf8");
|
|
16287
|
+
} else {
|
|
16288
|
+
throw new Error("Ledger not initialized. Call initLedger() first.");
|
|
16289
|
+
}
|
|
16290
|
+
fs4.renameSync(tempPath, ledgerPath);
|
|
16291
|
+
return event;
|
|
16292
|
+
}
|
|
16293
|
+
async function takeSnapshotEvent(directory, plan, options) {
|
|
16294
|
+
const payloadHash = computePlanHash(plan);
|
|
16295
|
+
const snapshotPayload = {
|
|
16296
|
+
plan,
|
|
16297
|
+
payload_hash: payloadHash
|
|
16298
|
+
};
|
|
16299
|
+
return appendLedgerEvent(directory, {
|
|
16300
|
+
event_type: "snapshot",
|
|
16301
|
+
source: "takeSnapshotEvent",
|
|
16302
|
+
plan_id: plan.title,
|
|
16303
|
+
payload: snapshotPayload
|
|
16304
|
+
}, options);
|
|
16305
|
+
}
|
|
16306
|
+
async function replayFromLedger(directory, options) {
|
|
16307
|
+
const events = await readLedgerEvents(directory);
|
|
16308
|
+
if (events.length === 0) {
|
|
16309
|
+
return null;
|
|
16310
|
+
}
|
|
16311
|
+
{
|
|
16312
|
+
const snapshotEvents = events.filter((e) => e.event_type === "snapshot");
|
|
16313
|
+
if (snapshotEvents.length > 0) {
|
|
16314
|
+
const latestSnapshotEvent = snapshotEvents[snapshotEvents.length - 1];
|
|
16315
|
+
const snapshotPayload = latestSnapshotEvent.payload;
|
|
16316
|
+
let plan2 = snapshotPayload.plan;
|
|
16317
|
+
const eventsAfterSnapshot = events.filter((e) => e.seq > latestSnapshotEvent.seq);
|
|
16318
|
+
for (const event of eventsAfterSnapshot) {
|
|
16319
|
+
plan2 = applyEventToPlan(plan2, event);
|
|
16320
|
+
if (plan2 === null) {
|
|
16321
|
+
return null;
|
|
16322
|
+
}
|
|
16323
|
+
}
|
|
16324
|
+
return plan2;
|
|
16325
|
+
}
|
|
16326
|
+
}
|
|
16327
|
+
const planJsonPath = getPlanJsonPath(directory);
|
|
16328
|
+
if (!fs4.existsSync(planJsonPath)) {
|
|
16329
|
+
return null;
|
|
16330
|
+
}
|
|
16331
|
+
let plan;
|
|
16332
|
+
try {
|
|
16333
|
+
const content = fs4.readFileSync(planJsonPath, "utf8");
|
|
16334
|
+
plan = JSON.parse(content);
|
|
16335
|
+
} catch {
|
|
16336
|
+
return null;
|
|
16337
|
+
}
|
|
16338
|
+
for (const event of events) {
|
|
16339
|
+
if (plan === null) {
|
|
16340
|
+
return null;
|
|
16341
|
+
}
|
|
16342
|
+
plan = applyEventToPlan(plan, event);
|
|
16343
|
+
}
|
|
16344
|
+
return plan;
|
|
16345
|
+
}
|
|
16346
|
+
function applyEventToPlan(plan, event) {
|
|
16347
|
+
switch (event.event_type) {
|
|
16348
|
+
case "plan_created":
|
|
16349
|
+
return plan;
|
|
16350
|
+
case "task_status_changed":
|
|
16351
|
+
if (event.task_id && event.to_status) {
|
|
16352
|
+
for (const phase of plan.phases) {
|
|
16353
|
+
const task = phase.tasks.find((t) => t.id === event.task_id);
|
|
16354
|
+
if (task) {
|
|
16355
|
+
task.status = event.to_status;
|
|
16356
|
+
break;
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
16359
|
+
}
|
|
16360
|
+
return plan;
|
|
16361
|
+
case "phase_completed":
|
|
16362
|
+
if (event.phase_id) {
|
|
16363
|
+
const phase = plan.phases.find((p) => p.id === event.phase_id);
|
|
16364
|
+
if (phase) {
|
|
16365
|
+
phase.status = "complete";
|
|
16366
|
+
}
|
|
16367
|
+
}
|
|
16368
|
+
return plan;
|
|
16369
|
+
case "plan_exported":
|
|
16370
|
+
return plan;
|
|
16371
|
+
case "task_added":
|
|
16372
|
+
return plan;
|
|
16373
|
+
case "task_updated":
|
|
16374
|
+
return plan;
|
|
16375
|
+
case "plan_rebuilt":
|
|
16376
|
+
return plan;
|
|
16377
|
+
case "task_reordered":
|
|
16378
|
+
return plan;
|
|
16379
|
+
case "snapshot":
|
|
16380
|
+
return plan;
|
|
16381
|
+
case "plan_reset":
|
|
16382
|
+
return null;
|
|
16383
|
+
default:
|
|
16384
|
+
throw new Error(`applyEventToPlan: unhandled event type "${event.event_type}" at seq ${event.seq}`);
|
|
16385
|
+
}
|
|
16386
|
+
}
|
|
16387
|
+
var LEDGER_SCHEMA_VERSION = "1.0.0", LEDGER_FILENAME = "plan-ledger.jsonl", PLAN_JSON_FILENAME = "plan.json", LedgerStaleWriterError;
|
|
16388
|
+
var init_ledger = __esm(() => {
|
|
16389
|
+
LedgerStaleWriterError = class LedgerStaleWriterError extends Error {
|
|
16390
|
+
constructor(message) {
|
|
16391
|
+
super(message);
|
|
16392
|
+
this.name = "LedgerStaleWriterError";
|
|
16393
|
+
}
|
|
16394
|
+
};
|
|
16395
|
+
});
|
|
16396
|
+
|
|
16128
16397
|
// src/plan/manager.ts
|
|
16129
16398
|
import { renameSync as renameSync3, unlinkSync } from "fs";
|
|
16130
|
-
import * as
|
|
16399
|
+
import * as path8 from "path";
|
|
16131
16400
|
async function loadPlanJsonOnly(directory) {
|
|
16132
16401
|
const planJsonContent = await readSwarmFileAsync(directory, "plan.json");
|
|
16133
16402
|
if (planJsonContent !== null) {
|
|
@@ -16158,6 +16427,17 @@ function compareTaskIds(a, b) {
|
|
|
16158
16427
|
}
|
|
16159
16428
|
return 0;
|
|
16160
16429
|
}
|
|
16430
|
+
async function getLatestLedgerHash(directory) {
|
|
16431
|
+
try {
|
|
16432
|
+
const events = await readLedgerEvents(directory);
|
|
16433
|
+
if (events.length === 0)
|
|
16434
|
+
return "";
|
|
16435
|
+
const lastEvent = events[events.length - 1];
|
|
16436
|
+
return lastEvent.plan_hash_after;
|
|
16437
|
+
} catch {
|
|
16438
|
+
return "";
|
|
16439
|
+
}
|
|
16440
|
+
}
|
|
16161
16441
|
function computePlanContentHash(plan) {
|
|
16162
16442
|
const content = {
|
|
16163
16443
|
schema_version: plan.schema_version,
|
|
@@ -16209,13 +16489,13 @@ async function isPlanMdInSync(directory, plan) {
|
|
|
16209
16489
|
return normalizedActual.includes(normalizedExpected) || normalizedExpected.includes(normalizedActual.replace(/^#.*$/gm, "").trim());
|
|
16210
16490
|
}
|
|
16211
16491
|
async function regeneratePlanMarkdown(directory, plan) {
|
|
16212
|
-
const swarmDir =
|
|
16492
|
+
const swarmDir = path8.resolve(directory, ".swarm");
|
|
16213
16493
|
const contentHash = computePlanContentHash(plan);
|
|
16214
16494
|
const markdown = derivePlanMarkdown(plan);
|
|
16215
16495
|
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
16216
16496
|
${markdown}`;
|
|
16217
|
-
const mdPath =
|
|
16218
|
-
const mdTempPath =
|
|
16497
|
+
const mdPath = path8.join(swarmDir, "plan.md");
|
|
16498
|
+
const mdTempPath = path8.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
16219
16499
|
try {
|
|
16220
16500
|
await Bun.write(mdTempPath, markdownWithHash);
|
|
16221
16501
|
renameSync3(mdTempPath, mdPath);
|
|
@@ -16242,9 +16522,34 @@ async function loadPlan(directory) {
|
|
|
16242
16522
|
warn(`Failed to regenerate plan.md: ${regenError instanceof Error ? regenError.message : String(regenError)}. Proceeding with plan.json only.`);
|
|
16243
16523
|
}
|
|
16244
16524
|
}
|
|
16525
|
+
if (await ledgerExists(directory)) {
|
|
16526
|
+
const planHash = computePlanHash(validated);
|
|
16527
|
+
const ledgerHash = await getLatestLedgerHash(directory);
|
|
16528
|
+
if (ledgerHash !== "" && planHash !== ledgerHash) {
|
|
16529
|
+
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.");
|
|
16530
|
+
try {
|
|
16531
|
+
const rebuilt = await replayFromLedger(directory);
|
|
16532
|
+
if (rebuilt) {
|
|
16533
|
+
await rebuildPlan(directory, rebuilt);
|
|
16534
|
+
warn("[loadPlan] Rebuilt plan from ledger. Checkpoint available at SWARM_PLAN.md if it exists.");
|
|
16535
|
+
return rebuilt;
|
|
16536
|
+
}
|
|
16537
|
+
} catch (replayError) {
|
|
16538
|
+
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.`);
|
|
16539
|
+
}
|
|
16540
|
+
}
|
|
16541
|
+
}
|
|
16245
16542
|
return validated;
|
|
16246
16543
|
} catch (error93) {
|
|
16247
|
-
warn(`
|
|
16544
|
+
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.`);
|
|
16545
|
+
if (await ledgerExists(directory)) {
|
|
16546
|
+
const rebuilt = await replayFromLedger(directory);
|
|
16547
|
+
if (rebuilt) {
|
|
16548
|
+
await rebuildPlan(directory, rebuilt);
|
|
16549
|
+
warn("[loadPlan] Rebuilt plan from ledger after validation failure. Projection was stale.");
|
|
16550
|
+
return rebuilt;
|
|
16551
|
+
}
|
|
16552
|
+
}
|
|
16248
16553
|
const planMdContent2 = await readSwarmFileAsync(directory, "plan.md");
|
|
16249
16554
|
if (planMdContent2 !== null) {
|
|
16250
16555
|
const migrated = migrateLegacyPlan(planMdContent2);
|
|
@@ -16260,6 +16565,13 @@ async function loadPlan(directory) {
|
|
|
16260
16565
|
await savePlan(directory, migrated);
|
|
16261
16566
|
return migrated;
|
|
16262
16567
|
}
|
|
16568
|
+
if (await ledgerExists(directory)) {
|
|
16569
|
+
const rebuilt = await replayFromLedger(directory);
|
|
16570
|
+
if (rebuilt) {
|
|
16571
|
+
await savePlan(directory, rebuilt);
|
|
16572
|
+
return rebuilt;
|
|
16573
|
+
}
|
|
16574
|
+
}
|
|
16263
16575
|
return null;
|
|
16264
16576
|
}
|
|
16265
16577
|
async function savePlan(directory, plan, options) {
|
|
@@ -16269,10 +16581,10 @@ async function savePlan(directory, plan, options) {
|
|
|
16269
16581
|
const validated = PlanSchema.parse(plan);
|
|
16270
16582
|
if (options?.preserveCompletedStatuses !== false) {
|
|
16271
16583
|
try {
|
|
16272
|
-
const
|
|
16273
|
-
if (
|
|
16584
|
+
const currentPlan2 = await loadPlanJsonOnly(directory);
|
|
16585
|
+
if (currentPlan2) {
|
|
16274
16586
|
const completedTaskIds = new Set;
|
|
16275
|
-
for (const phase of
|
|
16587
|
+
for (const phase of currentPlan2.phases) {
|
|
16276
16588
|
for (const task of phase.tasks) {
|
|
16277
16589
|
if (task.status === "completed")
|
|
16278
16590
|
completedTaskIds.add(task.id);
|
|
@@ -16302,9 +16614,58 @@ async function savePlan(directory, plan, options) {
|
|
|
16302
16614
|
phase.status = "pending";
|
|
16303
16615
|
}
|
|
16304
16616
|
}
|
|
16305
|
-
const
|
|
16306
|
-
|
|
16307
|
-
|
|
16617
|
+
const currentPlan = await loadPlanJsonOnly(directory);
|
|
16618
|
+
if (!await ledgerExists(directory)) {
|
|
16619
|
+
const planId = `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
16620
|
+
await initLedger(directory, planId);
|
|
16621
|
+
}
|
|
16622
|
+
const currentHash = computeCurrentPlanHash(directory);
|
|
16623
|
+
const hashAfter = computePlanHash(validated);
|
|
16624
|
+
if (currentPlan) {
|
|
16625
|
+
const oldTaskMap = new Map;
|
|
16626
|
+
for (const phase of currentPlan.phases) {
|
|
16627
|
+
for (const task of phase.tasks) {
|
|
16628
|
+
oldTaskMap.set(task.id, { phase: task.phase, status: task.status });
|
|
16629
|
+
}
|
|
16630
|
+
}
|
|
16631
|
+
try {
|
|
16632
|
+
for (const phase of validated.phases) {
|
|
16633
|
+
for (const task of phase.tasks) {
|
|
16634
|
+
const oldTask = oldTaskMap.get(task.id);
|
|
16635
|
+
if (oldTask && oldTask.status !== task.status) {
|
|
16636
|
+
const eventInput = {
|
|
16637
|
+
plan_id: `${validated.swarm}-${validated.title}`.replace(/[^a-zA-Z0-9-_]/g, "_"),
|
|
16638
|
+
event_type: "task_status_changed",
|
|
16639
|
+
task_id: task.id,
|
|
16640
|
+
phase_id: phase.id,
|
|
16641
|
+
from_status: oldTask.status,
|
|
16642
|
+
to_status: task.status,
|
|
16643
|
+
source: "savePlan"
|
|
16644
|
+
};
|
|
16645
|
+
await appendLedgerEvent(directory, eventInput, {
|
|
16646
|
+
expectedHash: currentHash,
|
|
16647
|
+
planHashAfter: hashAfter
|
|
16648
|
+
});
|
|
16649
|
+
}
|
|
16650
|
+
}
|
|
16651
|
+
}
|
|
16652
|
+
} catch (error93) {
|
|
16653
|
+
if (error93 instanceof LedgerStaleWriterError) {
|
|
16654
|
+
throw new Error(`Concurrent plan modification detected: ${error93.message}. Please retry the operation.`);
|
|
16655
|
+
}
|
|
16656
|
+
throw error93;
|
|
16657
|
+
}
|
|
16658
|
+
}
|
|
16659
|
+
const SNAPSHOT_INTERVAL = 50;
|
|
16660
|
+
const latestSeq = await getLatestLedgerSeq(directory);
|
|
16661
|
+
if (latestSeq > 0 && latestSeq % SNAPSHOT_INTERVAL === 0) {
|
|
16662
|
+
await takeSnapshotEvent(directory, validated, {
|
|
16663
|
+
planHashAfter: hashAfter
|
|
16664
|
+
}).catch(() => {});
|
|
16665
|
+
}
|
|
16666
|
+
const swarmDir = path8.resolve(directory, ".swarm");
|
|
16667
|
+
const planPath = path8.join(swarmDir, "plan.json");
|
|
16668
|
+
const tempPath = path8.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
16308
16669
|
try {
|
|
16309
16670
|
await Bun.write(tempPath, JSON.stringify(validated, null, 2));
|
|
16310
16671
|
renameSync3(tempPath, planPath);
|
|
@@ -16317,8 +16678,8 @@ async function savePlan(directory, plan, options) {
|
|
|
16317
16678
|
const markdown = derivePlanMarkdown(validated);
|
|
16318
16679
|
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
16319
16680
|
${markdown}`;
|
|
16320
|
-
const mdPath =
|
|
16321
|
-
const mdTempPath =
|
|
16681
|
+
const mdPath = path8.join(swarmDir, "plan.md");
|
|
16682
|
+
const mdTempPath = path8.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
16322
16683
|
try {
|
|
16323
16684
|
await Bun.write(mdTempPath, markdownWithHash);
|
|
16324
16685
|
renameSync3(mdTempPath, mdPath);
|
|
@@ -16328,7 +16689,7 @@ ${markdown}`;
|
|
|
16328
16689
|
} catch {}
|
|
16329
16690
|
}
|
|
16330
16691
|
try {
|
|
16331
|
-
const markerPath =
|
|
16692
|
+
const markerPath = path8.join(swarmDir, ".plan-write-marker");
|
|
16332
16693
|
const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
16333
16694
|
const marker = JSON.stringify({
|
|
16334
16695
|
source: "plan_manager",
|
|
@@ -16339,6 +16700,22 @@ ${markdown}`;
|
|
|
16339
16700
|
await Bun.write(markerPath, marker);
|
|
16340
16701
|
} catch {}
|
|
16341
16702
|
}
|
|
16703
|
+
async function rebuildPlan(directory, plan) {
|
|
16704
|
+
const targetPlan = plan ?? await replayFromLedger(directory);
|
|
16705
|
+
if (!targetPlan)
|
|
16706
|
+
return null;
|
|
16707
|
+
const swarmDir = path8.join(directory, ".swarm");
|
|
16708
|
+
const planPath = path8.join(swarmDir, "plan.json");
|
|
16709
|
+
const mdPath = path8.join(swarmDir, "plan.md");
|
|
16710
|
+
const tempPlanPath = path8.join(swarmDir, `plan.json.rebuild.${Date.now()}`);
|
|
16711
|
+
await Bun.write(tempPlanPath, JSON.stringify(targetPlan, null, 2));
|
|
16712
|
+
renameSync3(tempPlanPath, planPath);
|
|
16713
|
+
const markdown = derivePlanMarkdown(targetPlan);
|
|
16714
|
+
const tempMdPath = path8.join(swarmDir, `plan.md.rebuild.${Date.now()}`);
|
|
16715
|
+
await Bun.write(tempMdPath, markdown);
|
|
16716
|
+
renameSync3(tempMdPath, mdPath);
|
|
16717
|
+
return targetPlan;
|
|
16718
|
+
}
|
|
16342
16719
|
function derivePlanMarkdown(plan) {
|
|
16343
16720
|
const statusMap = {
|
|
16344
16721
|
pending: "PENDING",
|
|
@@ -16609,6 +16986,7 @@ var init_manager2 = __esm(() => {
|
|
|
16609
16986
|
init_plan_schema();
|
|
16610
16987
|
init_utils2();
|
|
16611
16988
|
init_utils();
|
|
16989
|
+
init_ledger();
|
|
16612
16990
|
});
|
|
16613
16991
|
|
|
16614
16992
|
// src/services/config-doctor.ts
|
|
@@ -16624,20 +17002,20 @@ __export(exports_config_doctor, {
|
|
|
16624
17002
|
createConfigBackup: () => createConfigBackup,
|
|
16625
17003
|
applySafeAutoFixes: () => applySafeAutoFixes
|
|
16626
17004
|
});
|
|
16627
|
-
import * as
|
|
16628
|
-
import * as
|
|
17005
|
+
import * as crypto3 from "crypto";
|
|
17006
|
+
import * as fs7 from "fs";
|
|
16629
17007
|
import * as os4 from "os";
|
|
16630
|
-
import * as
|
|
17008
|
+
import * as path16 from "path";
|
|
16631
17009
|
function getUserConfigDir3() {
|
|
16632
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
17010
|
+
return process.env.XDG_CONFIG_HOME || path16.join(os4.homedir(), ".config");
|
|
16633
17011
|
}
|
|
16634
17012
|
function getConfigPaths(directory) {
|
|
16635
|
-
const userConfigPath =
|
|
16636
|
-
const projectConfigPath =
|
|
17013
|
+
const userConfigPath = path16.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
|
|
17014
|
+
const projectConfigPath = path16.join(directory, ".opencode", "opencode-swarm.json");
|
|
16637
17015
|
return { userConfigPath, projectConfigPath };
|
|
16638
17016
|
}
|
|
16639
17017
|
function computeHash(content) {
|
|
16640
|
-
return
|
|
17018
|
+
return crypto3.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
16641
17019
|
}
|
|
16642
17020
|
function isValidConfigPath(configPath, directory) {
|
|
16643
17021
|
const normalizedPath = configPath.replace(/\\/g, "/");
|
|
@@ -16658,9 +17036,9 @@ function isValidConfigPath(configPath, directory) {
|
|
|
16658
17036
|
const normalizedUser = userConfigPath.replace(/\\/g, "/");
|
|
16659
17037
|
const normalizedProject = projectConfigPath.replace(/\\/g, "/");
|
|
16660
17038
|
try {
|
|
16661
|
-
const resolvedConfig =
|
|
16662
|
-
const resolvedUser =
|
|
16663
|
-
const resolvedProject =
|
|
17039
|
+
const resolvedConfig = path16.resolve(configPath);
|
|
17040
|
+
const resolvedUser = path16.resolve(normalizedUser);
|
|
17041
|
+
const resolvedProject = path16.resolve(normalizedProject);
|
|
16664
17042
|
return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
|
|
16665
17043
|
} catch {
|
|
16666
17044
|
return false;
|
|
@@ -16670,19 +17048,19 @@ function createConfigBackup(directory) {
|
|
|
16670
17048
|
const { userConfigPath, projectConfigPath } = getConfigPaths(directory);
|
|
16671
17049
|
let configPath = projectConfigPath;
|
|
16672
17050
|
let content = null;
|
|
16673
|
-
if (
|
|
17051
|
+
if (fs7.existsSync(projectConfigPath)) {
|
|
16674
17052
|
try {
|
|
16675
|
-
content =
|
|
17053
|
+
content = fs7.readFileSync(projectConfigPath, "utf-8");
|
|
16676
17054
|
} catch (error93) {
|
|
16677
17055
|
log("[ConfigDoctor] project config read failed", {
|
|
16678
17056
|
error: error93 instanceof Error ? error93.message : String(error93)
|
|
16679
17057
|
});
|
|
16680
17058
|
}
|
|
16681
17059
|
}
|
|
16682
|
-
if (content === null &&
|
|
17060
|
+
if (content === null && fs7.existsSync(userConfigPath)) {
|
|
16683
17061
|
configPath = userConfigPath;
|
|
16684
17062
|
try {
|
|
16685
|
-
content =
|
|
17063
|
+
content = fs7.readFileSync(userConfigPath, "utf-8");
|
|
16686
17064
|
} catch (error93) {
|
|
16687
17065
|
log("[ConfigDoctor] user config read failed", {
|
|
16688
17066
|
error: error93 instanceof Error ? error93.message : String(error93)
|
|
@@ -16700,12 +17078,12 @@ function createConfigBackup(directory) {
|
|
|
16700
17078
|
};
|
|
16701
17079
|
}
|
|
16702
17080
|
function writeBackupArtifact(directory, backup) {
|
|
16703
|
-
const swarmDir =
|
|
16704
|
-
if (!
|
|
16705
|
-
|
|
17081
|
+
const swarmDir = path16.join(directory, ".swarm");
|
|
17082
|
+
if (!fs7.existsSync(swarmDir)) {
|
|
17083
|
+
fs7.mkdirSync(swarmDir, { recursive: true });
|
|
16706
17084
|
}
|
|
16707
17085
|
const backupFilename = `config-backup-${backup.createdAt}.json`;
|
|
16708
|
-
const backupPath =
|
|
17086
|
+
const backupPath = path16.join(swarmDir, backupFilename);
|
|
16709
17087
|
const artifact = {
|
|
16710
17088
|
createdAt: backup.createdAt,
|
|
16711
17089
|
configPath: backup.configPath,
|
|
@@ -16713,15 +17091,15 @@ function writeBackupArtifact(directory, backup) {
|
|
|
16713
17091
|
content: backup.content,
|
|
16714
17092
|
preview: backup.content.substring(0, 500) + (backup.content.length > 500 ? "..." : "")
|
|
16715
17093
|
};
|
|
16716
|
-
|
|
17094
|
+
fs7.writeFileSync(backupPath, JSON.stringify(artifact, null, 2), "utf-8");
|
|
16717
17095
|
return backupPath;
|
|
16718
17096
|
}
|
|
16719
17097
|
function restoreFromBackup(backupPath, directory) {
|
|
16720
|
-
if (!
|
|
17098
|
+
if (!fs7.existsSync(backupPath)) {
|
|
16721
17099
|
return null;
|
|
16722
17100
|
}
|
|
16723
17101
|
try {
|
|
16724
|
-
const artifact = JSON.parse(
|
|
17102
|
+
const artifact = JSON.parse(fs7.readFileSync(backupPath, "utf-8"));
|
|
16725
17103
|
if (!artifact.content || !artifact.configPath || !artifact.contentHash) {
|
|
16726
17104
|
return null;
|
|
16727
17105
|
}
|
|
@@ -16735,11 +17113,11 @@ function restoreFromBackup(backupPath, directory) {
|
|
|
16735
17113
|
return null;
|
|
16736
17114
|
}
|
|
16737
17115
|
const targetPath = artifact.configPath;
|
|
16738
|
-
const targetDir =
|
|
16739
|
-
if (!
|
|
16740
|
-
|
|
17116
|
+
const targetDir = path16.dirname(targetPath);
|
|
17117
|
+
if (!fs7.existsSync(targetDir)) {
|
|
17118
|
+
fs7.mkdirSync(targetDir, { recursive: true });
|
|
16741
17119
|
}
|
|
16742
|
-
|
|
17120
|
+
fs7.writeFileSync(targetPath, artifact.content, "utf-8");
|
|
16743
17121
|
return targetPath;
|
|
16744
17122
|
} catch {
|
|
16745
17123
|
return null;
|
|
@@ -16749,12 +17127,12 @@ function readConfigFromFile(directory) {
|
|
|
16749
17127
|
const { userConfigPath, projectConfigPath } = getConfigPaths(directory);
|
|
16750
17128
|
let configPath = projectConfigPath;
|
|
16751
17129
|
let configContent = null;
|
|
16752
|
-
if (
|
|
17130
|
+
if (fs7.existsSync(projectConfigPath)) {
|
|
16753
17131
|
configPath = projectConfigPath;
|
|
16754
|
-
configContent =
|
|
16755
|
-
} else if (
|
|
17132
|
+
configContent = fs7.readFileSync(projectConfigPath, "utf-8");
|
|
17133
|
+
} else if (fs7.existsSync(userConfigPath)) {
|
|
16756
17134
|
configPath = userConfigPath;
|
|
16757
|
-
configContent =
|
|
17135
|
+
configContent = fs7.readFileSync(userConfigPath, "utf-8");
|
|
16758
17136
|
}
|
|
16759
17137
|
if (configContent === null) {
|
|
16760
17138
|
return null;
|
|
@@ -16766,9 +17144,9 @@ function readConfigFromFile(directory) {
|
|
|
16766
17144
|
return null;
|
|
16767
17145
|
}
|
|
16768
17146
|
}
|
|
16769
|
-
function validateConfigKey(
|
|
17147
|
+
function validateConfigKey(path17, value, _config) {
|
|
16770
17148
|
const findings = [];
|
|
16771
|
-
switch (
|
|
17149
|
+
switch (path17) {
|
|
16772
17150
|
case "agents": {
|
|
16773
17151
|
if (value !== undefined) {
|
|
16774
17152
|
findings.push({
|
|
@@ -17015,27 +17393,27 @@ function validateConfigKey(path15, value, _config) {
|
|
|
17015
17393
|
}
|
|
17016
17394
|
return findings;
|
|
17017
17395
|
}
|
|
17018
|
-
function walkConfigAndValidate(obj,
|
|
17396
|
+
function walkConfigAndValidate(obj, path17, config3, findings) {
|
|
17019
17397
|
if (obj === null || obj === undefined) {
|
|
17020
17398
|
return;
|
|
17021
17399
|
}
|
|
17022
|
-
if (
|
|
17023
|
-
const keyFindings = validateConfigKey(
|
|
17400
|
+
if (path17 && typeof obj === "object" && !Array.isArray(obj)) {
|
|
17401
|
+
const keyFindings = validateConfigKey(path17, obj, config3);
|
|
17024
17402
|
findings.push(...keyFindings);
|
|
17025
17403
|
}
|
|
17026
17404
|
if (typeof obj !== "object") {
|
|
17027
|
-
const keyFindings = validateConfigKey(
|
|
17405
|
+
const keyFindings = validateConfigKey(path17, obj, config3);
|
|
17028
17406
|
findings.push(...keyFindings);
|
|
17029
17407
|
return;
|
|
17030
17408
|
}
|
|
17031
17409
|
if (Array.isArray(obj)) {
|
|
17032
17410
|
obj.forEach((item, index) => {
|
|
17033
|
-
walkConfigAndValidate(item, `${
|
|
17411
|
+
walkConfigAndValidate(item, `${path17}[${index}]`, config3, findings);
|
|
17034
17412
|
});
|
|
17035
17413
|
return;
|
|
17036
17414
|
}
|
|
17037
17415
|
for (const [key, value] of Object.entries(obj)) {
|
|
17038
|
-
const newPath =
|
|
17416
|
+
const newPath = path17 ? `${path17}.${key}` : key;
|
|
17039
17417
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
17040
17418
|
}
|
|
17041
17419
|
}
|
|
@@ -17050,9 +17428,9 @@ function runConfigDoctor(config3, directory) {
|
|
|
17050
17428
|
const hasAutoFixableIssues = findings.some((f) => f.autoFixable && f.proposedFix?.risk === "low");
|
|
17051
17429
|
const { userConfigPath, projectConfigPath } = getConfigPaths(directory);
|
|
17052
17430
|
let configSource = "defaults";
|
|
17053
|
-
if (
|
|
17431
|
+
if (fs7.existsSync(projectConfigPath)) {
|
|
17054
17432
|
configSource = projectConfigPath;
|
|
17055
|
-
} else if (
|
|
17433
|
+
} else if (fs7.existsSync(userConfigPath)) {
|
|
17056
17434
|
configSource = userConfigPath;
|
|
17057
17435
|
}
|
|
17058
17436
|
return {
|
|
@@ -17081,12 +17459,12 @@ function applySafeAutoFixes(directory, result) {
|
|
|
17081
17459
|
const { userConfigPath, projectConfigPath } = getConfigPaths(directory);
|
|
17082
17460
|
let configPath = projectConfigPath;
|
|
17083
17461
|
let configContent;
|
|
17084
|
-
if (
|
|
17462
|
+
if (fs7.existsSync(projectConfigPath)) {
|
|
17085
17463
|
configPath = projectConfigPath;
|
|
17086
|
-
configContent =
|
|
17087
|
-
} else if (
|
|
17464
|
+
configContent = fs7.readFileSync(projectConfigPath, "utf-8");
|
|
17465
|
+
} else if (fs7.existsSync(userConfigPath)) {
|
|
17088
17466
|
configPath = userConfigPath;
|
|
17089
|
-
configContent =
|
|
17467
|
+
configContent = fs7.readFileSync(userConfigPath, "utf-8");
|
|
17090
17468
|
} else {
|
|
17091
17469
|
return { appliedFixes, updatedConfigPath: null };
|
|
17092
17470
|
}
|
|
@@ -17155,22 +17533,22 @@ function applySafeAutoFixes(directory, result) {
|
|
|
17155
17533
|
}
|
|
17156
17534
|
}
|
|
17157
17535
|
if (appliedFixes.length > 0) {
|
|
17158
|
-
const configDir =
|
|
17159
|
-
if (!
|
|
17160
|
-
|
|
17536
|
+
const configDir = path16.dirname(configPath);
|
|
17537
|
+
if (!fs7.existsSync(configDir)) {
|
|
17538
|
+
fs7.mkdirSync(configDir, { recursive: true });
|
|
17161
17539
|
}
|
|
17162
|
-
|
|
17540
|
+
fs7.writeFileSync(configPath, JSON.stringify(config3, null, 2), "utf-8");
|
|
17163
17541
|
updatedConfigPath = configPath;
|
|
17164
17542
|
}
|
|
17165
17543
|
return { appliedFixes, updatedConfigPath };
|
|
17166
17544
|
}
|
|
17167
17545
|
function writeDoctorArtifact(directory, result) {
|
|
17168
|
-
const swarmDir =
|
|
17169
|
-
if (!
|
|
17170
|
-
|
|
17546
|
+
const swarmDir = path16.join(directory, ".swarm");
|
|
17547
|
+
if (!fs7.existsSync(swarmDir)) {
|
|
17548
|
+
fs7.mkdirSync(swarmDir, { recursive: true });
|
|
17171
17549
|
}
|
|
17172
17550
|
const artifactFilename = "config-doctor.json";
|
|
17173
|
-
const artifactPath =
|
|
17551
|
+
const artifactPath = path16.join(swarmDir, artifactFilename);
|
|
17174
17552
|
const guiOutput = {
|
|
17175
17553
|
timestamp: result.timestamp,
|
|
17176
17554
|
summary: result.summary,
|
|
@@ -17191,7 +17569,7 @@ function writeDoctorArtifact(directory, result) {
|
|
|
17191
17569
|
} : null
|
|
17192
17570
|
}))
|
|
17193
17571
|
};
|
|
17194
|
-
|
|
17572
|
+
fs7.writeFileSync(artifactPath, JSON.stringify(guiOutput, null, 2), "utf-8");
|
|
17195
17573
|
return artifactPath;
|
|
17196
17574
|
}
|
|
17197
17575
|
function shouldRunOnStartup(automationConfig) {
|
|
@@ -17531,9 +17909,9 @@ var init_evidence_summary_service = __esm(() => {
|
|
|
17531
17909
|
});
|
|
17532
17910
|
|
|
17533
17911
|
// src/cli/index.ts
|
|
17534
|
-
import * as
|
|
17912
|
+
import * as fs17 from "fs";
|
|
17535
17913
|
import * as os5 from "os";
|
|
17536
|
-
import * as
|
|
17914
|
+
import * as path27 from "path";
|
|
17537
17915
|
|
|
17538
17916
|
// src/commands/agents.ts
|
|
17539
17917
|
function handleAgentsCommand(agents, guardrails) {
|
|
@@ -18253,7 +18631,8 @@ var CuratorConfigSchema = exports_external.object({
|
|
|
18253
18631
|
min_knowledge_confidence: exports_external.number().min(0).max(1).default(0.7),
|
|
18254
18632
|
compliance_report: exports_external.boolean().default(true),
|
|
18255
18633
|
suppress_warnings: exports_external.boolean().default(true),
|
|
18256
|
-
drift_inject_max_chars: exports_external.number().min(100).max(2000).default(500)
|
|
18634
|
+
drift_inject_max_chars: exports_external.number().min(100).max(2000).default(500),
|
|
18635
|
+
llm_timeout_ms: exports_external.number().int().min(5000).max(600000).default(300000)
|
|
18257
18636
|
});
|
|
18258
18637
|
var SlopDetectorConfigSchema = exports_external.object({
|
|
18259
18638
|
enabled: exports_external.boolean().default(true),
|
|
@@ -31619,7 +31998,7 @@ async function handleClarifyCommand(_directory, args) {
|
|
|
31619
31998
|
}
|
|
31620
31999
|
|
|
31621
32000
|
// src/commands/close.ts
|
|
31622
|
-
import { promises as
|
|
32001
|
+
import { promises as fs6 } from "fs";
|
|
31623
32002
|
init_manager();
|
|
31624
32003
|
|
|
31625
32004
|
// src/hooks/knowledge-store.ts
|
|
@@ -32255,10 +32634,31 @@ async function runAutoPromotion(directory, config3) {
|
|
|
32255
32634
|
// src/commands/close.ts
|
|
32256
32635
|
init_utils2();
|
|
32257
32636
|
|
|
32637
|
+
// src/plan/checkpoint.ts
|
|
32638
|
+
init_plan_schema();
|
|
32639
|
+
init_ledger();
|
|
32640
|
+
init_manager2();
|
|
32641
|
+
import * as fs5 from "fs";
|
|
32642
|
+
import * as path9 from "path";
|
|
32643
|
+
async function writeCheckpoint(directory) {
|
|
32644
|
+
try {
|
|
32645
|
+
const plan = await loadPlan(directory);
|
|
32646
|
+
if (!plan)
|
|
32647
|
+
return;
|
|
32648
|
+
const jsonPath = path9.join(directory, "SWARM_PLAN.json");
|
|
32649
|
+
const mdPath = path9.join(directory, "SWARM_PLAN.md");
|
|
32650
|
+
fs5.writeFileSync(jsonPath, JSON.stringify(plan, null, 2), "utf8");
|
|
32651
|
+
const md = derivePlanMarkdown(plan);
|
|
32652
|
+
fs5.writeFileSync(mdPath, md, "utf8");
|
|
32653
|
+
} catch (error93) {
|
|
32654
|
+
console.warn(`[checkpoint] Failed to write SWARM_PLAN checkpoint: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
32655
|
+
}
|
|
32656
|
+
}
|
|
32657
|
+
|
|
32258
32658
|
// src/session/snapshot-writer.ts
|
|
32259
32659
|
init_utils2();
|
|
32260
|
-
import { mkdirSync as
|
|
32261
|
-
import * as
|
|
32660
|
+
import { mkdirSync as mkdirSync4, renameSync as renameSync4 } from "fs";
|
|
32661
|
+
import * as path10 from "path";
|
|
32262
32662
|
init_utils();
|
|
32263
32663
|
var _writeInFlight = Promise.resolve();
|
|
32264
32664
|
function serializeAgentSession(s) {
|
|
@@ -32345,11 +32745,11 @@ async function writeSnapshot(directory, state) {
|
|
|
32345
32745
|
}
|
|
32346
32746
|
const content = JSON.stringify(snapshot, null, 2);
|
|
32347
32747
|
const resolvedPath = validateSwarmPath(directory, "session/state.json");
|
|
32348
|
-
const dir =
|
|
32349
|
-
|
|
32748
|
+
const dir = path10.dirname(resolvedPath);
|
|
32749
|
+
mkdirSync4(dir, { recursive: true });
|
|
32350
32750
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
32351
32751
|
await Bun.write(tempPath, content);
|
|
32352
|
-
|
|
32752
|
+
renameSync4(tempPath, resolvedPath);
|
|
32353
32753
|
} catch (error93) {
|
|
32354
32754
|
log("[snapshot-writer] write failed", {
|
|
32355
32755
|
error: error93 instanceof Error ? error93.message : String(error93)
|
|
@@ -32708,7 +33108,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32708
33108
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
32709
33109
|
let planData;
|
|
32710
33110
|
try {
|
|
32711
|
-
const content = await
|
|
33111
|
+
const content = await fs6.readFile(planPath, "utf-8");
|
|
32712
33112
|
planData = JSON.parse(content);
|
|
32713
33113
|
} catch (error93) {
|
|
32714
33114
|
return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
@@ -32775,7 +33175,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32775
33175
|
}
|
|
32776
33176
|
}
|
|
32777
33177
|
try {
|
|
32778
|
-
await
|
|
33178
|
+
await fs6.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
|
|
32779
33179
|
} catch (error93) {
|
|
32780
33180
|
console.warn("[close-command] Failed to write plan.json:", error93);
|
|
32781
33181
|
}
|
|
@@ -32807,7 +33207,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32807
33207
|
].join(`
|
|
32808
33208
|
`);
|
|
32809
33209
|
try {
|
|
32810
|
-
await
|
|
33210
|
+
await fs6.writeFile(closeSummaryPath, summaryContent, "utf-8");
|
|
32811
33211
|
} catch (error93) {
|
|
32812
33212
|
console.warn("[close-command] Failed to write close-summary.md:", error93);
|
|
32813
33213
|
}
|
|
@@ -32816,6 +33216,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32816
33216
|
} catch (error93) {
|
|
32817
33217
|
console.warn("[close-command] flushPendingSnapshot error:", error93);
|
|
32818
33218
|
}
|
|
33219
|
+
await writeCheckpoint(directory).catch(() => {});
|
|
32819
33220
|
swarmState.agentSessions.clear();
|
|
32820
33221
|
swarmState.delegationChains.clear();
|
|
32821
33222
|
const warningMsg = warnings.length > 0 ? ` Warnings: ${warnings.join("; ")}.` : "";
|
|
@@ -32824,14 +33225,14 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32824
33225
|
|
|
32825
33226
|
// src/commands/config.ts
|
|
32826
33227
|
import * as os3 from "os";
|
|
32827
|
-
import * as
|
|
33228
|
+
import * as path11 from "path";
|
|
32828
33229
|
function getUserConfigDir2() {
|
|
32829
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
33230
|
+
return process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config");
|
|
32830
33231
|
}
|
|
32831
33232
|
async function handleConfigCommand(directory, _args) {
|
|
32832
33233
|
const config3 = loadPluginConfig(directory);
|
|
32833
|
-
const userConfigPath =
|
|
32834
|
-
const projectConfigPath =
|
|
33234
|
+
const userConfigPath = path11.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
|
|
33235
|
+
const projectConfigPath = path11.join(directory, ".opencode", "opencode-swarm.json");
|
|
32835
33236
|
const lines = [
|
|
32836
33237
|
"## Swarm Configuration",
|
|
32837
33238
|
"",
|
|
@@ -32849,7 +33250,7 @@ async function handleConfigCommand(directory, _args) {
|
|
|
32849
33250
|
}
|
|
32850
33251
|
|
|
32851
33252
|
// src/hooks/hive-promoter.ts
|
|
32852
|
-
import
|
|
33253
|
+
import path12 from "path";
|
|
32853
33254
|
|
|
32854
33255
|
// src/background/event-bus.ts
|
|
32855
33256
|
init_utils();
|
|
@@ -32921,6 +33322,7 @@ function getGlobalEventBus() {
|
|
|
32921
33322
|
}
|
|
32922
33323
|
|
|
32923
33324
|
// src/hooks/curator.ts
|
|
33325
|
+
init_manager2();
|
|
32924
33326
|
init_utils2();
|
|
32925
33327
|
|
|
32926
33328
|
// src/hooks/hive-promoter.ts
|
|
@@ -33103,7 +33505,7 @@ async function promoteToHive(directory, lesson, category) {
|
|
|
33103
33505
|
schema_version: 1,
|
|
33104
33506
|
created_at: new Date().toISOString(),
|
|
33105
33507
|
updated_at: new Date().toISOString(),
|
|
33106
|
-
source_project:
|
|
33508
|
+
source_project: path12.basename(directory) || "unknown",
|
|
33107
33509
|
encounter_score: 1
|
|
33108
33510
|
};
|
|
33109
33511
|
await appendKnowledge(resolveHiveKnowledgePath(), newHiveEntry);
|
|
@@ -33181,13 +33583,13 @@ function formatCurationSummary(summary) {
|
|
|
33181
33583
|
}
|
|
33182
33584
|
|
|
33183
33585
|
// src/commands/dark-matter.ts
|
|
33184
|
-
import
|
|
33586
|
+
import path14 from "path";
|
|
33185
33587
|
|
|
33186
33588
|
// src/tools/co-change-analyzer.ts
|
|
33187
33589
|
import * as child_process from "child_process";
|
|
33188
33590
|
import { randomUUID } from "crypto";
|
|
33189
33591
|
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
33190
|
-
import * as
|
|
33592
|
+
import * as path13 from "path";
|
|
33191
33593
|
import { promisify } from "util";
|
|
33192
33594
|
function getExecFileAsync() {
|
|
33193
33595
|
return promisify(child_process.execFile);
|
|
@@ -33289,7 +33691,7 @@ async function scanSourceFiles(dir) {
|
|
|
33289
33691
|
try {
|
|
33290
33692
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
33291
33693
|
for (const entry of entries) {
|
|
33292
|
-
const fullPath =
|
|
33694
|
+
const fullPath = path13.join(dir, entry.name);
|
|
33293
33695
|
if (entry.isDirectory()) {
|
|
33294
33696
|
if (skipDirs.has(entry.name)) {
|
|
33295
33697
|
continue;
|
|
@@ -33297,7 +33699,7 @@ async function scanSourceFiles(dir) {
|
|
|
33297
33699
|
const subFiles = await scanSourceFiles(fullPath);
|
|
33298
33700
|
results.push(...subFiles);
|
|
33299
33701
|
} else if (entry.isFile()) {
|
|
33300
|
-
const ext =
|
|
33702
|
+
const ext = path13.extname(entry.name);
|
|
33301
33703
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
|
|
33302
33704
|
results.push(fullPath);
|
|
33303
33705
|
}
|
|
@@ -33319,8 +33721,8 @@ async function getStaticEdges(directory) {
|
|
|
33319
33721
|
continue;
|
|
33320
33722
|
}
|
|
33321
33723
|
try {
|
|
33322
|
-
const sourceDir =
|
|
33323
|
-
const resolvedPath =
|
|
33724
|
+
const sourceDir = path13.dirname(sourceFile);
|
|
33725
|
+
const resolvedPath = path13.resolve(sourceDir, importPath);
|
|
33324
33726
|
const extensions = [
|
|
33325
33727
|
"",
|
|
33326
33728
|
".ts",
|
|
@@ -33345,8 +33747,8 @@ async function getStaticEdges(directory) {
|
|
|
33345
33747
|
if (!targetFile) {
|
|
33346
33748
|
continue;
|
|
33347
33749
|
}
|
|
33348
|
-
const relSource =
|
|
33349
|
-
const relTarget =
|
|
33750
|
+
const relSource = path13.relative(directory, sourceFile).replace(/\\/g, "/");
|
|
33751
|
+
const relTarget = path13.relative(directory, targetFile).replace(/\\/g, "/");
|
|
33350
33752
|
const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
|
|
33351
33753
|
edges.add(key);
|
|
33352
33754
|
} catch {}
|
|
@@ -33358,7 +33760,7 @@ async function getStaticEdges(directory) {
|
|
|
33358
33760
|
function isTestImplementationPair(fileA, fileB) {
|
|
33359
33761
|
const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
|
|
33360
33762
|
const getBaseName = (filePath) => {
|
|
33361
|
-
const base =
|
|
33763
|
+
const base = path13.basename(filePath);
|
|
33362
33764
|
for (const pattern of testPatterns) {
|
|
33363
33765
|
if (base.endsWith(pattern)) {
|
|
33364
33766
|
return base.slice(0, -pattern.length);
|
|
@@ -33368,16 +33770,16 @@ function isTestImplementationPair(fileA, fileB) {
|
|
|
33368
33770
|
};
|
|
33369
33771
|
const baseA = getBaseName(fileA);
|
|
33370
33772
|
const baseB = getBaseName(fileB);
|
|
33371
|
-
return baseA === baseB && baseA !==
|
|
33773
|
+
return baseA === baseB && baseA !== path13.basename(fileA) && baseA !== path13.basename(fileB);
|
|
33372
33774
|
}
|
|
33373
33775
|
function hasSharedPrefix(fileA, fileB) {
|
|
33374
|
-
const dirA =
|
|
33375
|
-
const dirB =
|
|
33776
|
+
const dirA = path13.dirname(fileA);
|
|
33777
|
+
const dirB = path13.dirname(fileB);
|
|
33376
33778
|
if (dirA !== dirB) {
|
|
33377
33779
|
return false;
|
|
33378
33780
|
}
|
|
33379
|
-
const baseA =
|
|
33380
|
-
const baseB =
|
|
33781
|
+
const baseA = path13.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
33782
|
+
const baseB = path13.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
33381
33783
|
if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
|
|
33382
33784
|
return true;
|
|
33383
33785
|
}
|
|
@@ -33431,8 +33833,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
33431
33833
|
const entries = [];
|
|
33432
33834
|
const now = new Date().toISOString();
|
|
33433
33835
|
for (const pair of pairs.slice(0, 10)) {
|
|
33434
|
-
const baseA =
|
|
33435
|
-
const baseB =
|
|
33836
|
+
const baseA = path13.basename(pair.fileA);
|
|
33837
|
+
const baseB = path13.basename(pair.fileB);
|
|
33436
33838
|
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.`;
|
|
33437
33839
|
if (lesson.length > 280) {
|
|
33438
33840
|
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.`;
|
|
@@ -33542,7 +33944,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33542
33944
|
const output = formatDarkMatterOutput(pairs);
|
|
33543
33945
|
if (pairs.length > 0) {
|
|
33544
33946
|
try {
|
|
33545
|
-
const projectName =
|
|
33947
|
+
const projectName = path14.basename(path14.resolve(directory));
|
|
33546
33948
|
const entries = darkMatterToKnowledgeEntries(pairs, projectName);
|
|
33547
33949
|
if (entries.length > 0) {
|
|
33548
33950
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -33563,8 +33965,8 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33563
33965
|
|
|
33564
33966
|
// src/services/diagnose-service.ts
|
|
33565
33967
|
import { execSync } from "child_process";
|
|
33566
|
-
import { existsSync as
|
|
33567
|
-
import
|
|
33968
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
33969
|
+
import path15 from "path";
|
|
33568
33970
|
import { fileURLToPath } from "url";
|
|
33569
33971
|
init_manager();
|
|
33570
33972
|
init_utils2();
|
|
@@ -33800,7 +34202,7 @@ async function checkConfigBackups(directory) {
|
|
|
33800
34202
|
}
|
|
33801
34203
|
async function checkGitRepository(directory) {
|
|
33802
34204
|
try {
|
|
33803
|
-
if (!
|
|
34205
|
+
if (!existsSync5(directory) || !statSync3(directory).isDirectory()) {
|
|
33804
34206
|
return {
|
|
33805
34207
|
name: "Git Repository",
|
|
33806
34208
|
status: "\u274C",
|
|
@@ -33861,8 +34263,8 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
33861
34263
|
};
|
|
33862
34264
|
}
|
|
33863
34265
|
async function checkConfigParseability(directory) {
|
|
33864
|
-
const configPath =
|
|
33865
|
-
if (!
|
|
34266
|
+
const configPath = path15.join(directory, ".opencode/opencode-swarm.json");
|
|
34267
|
+
if (!existsSync5(configPath)) {
|
|
33866
34268
|
return {
|
|
33867
34269
|
name: "Config Parseability",
|
|
33868
34270
|
status: "\u2705",
|
|
@@ -33870,7 +34272,7 @@ async function checkConfigParseability(directory) {
|
|
|
33870
34272
|
};
|
|
33871
34273
|
}
|
|
33872
34274
|
try {
|
|
33873
|
-
const content =
|
|
34275
|
+
const content = readFileSync5(configPath, "utf-8");
|
|
33874
34276
|
JSON.parse(content);
|
|
33875
34277
|
return {
|
|
33876
34278
|
name: "Config Parseability",
|
|
@@ -33908,15 +34310,15 @@ async function checkGrammarWasmFiles() {
|
|
|
33908
34310
|
"tree-sitter-ini.wasm",
|
|
33909
34311
|
"tree-sitter-regex.wasm"
|
|
33910
34312
|
];
|
|
33911
|
-
const thisDir =
|
|
34313
|
+
const thisDir = path15.dirname(fileURLToPath(import.meta.url));
|
|
33912
34314
|
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
33913
|
-
const grammarDir = isSource ?
|
|
34315
|
+
const grammarDir = isSource ? path15.join(thisDir, "..", "lang", "grammars") : path15.join(thisDir, "lang", "grammars");
|
|
33914
34316
|
const missing = [];
|
|
33915
|
-
if (!
|
|
34317
|
+
if (!existsSync5(path15.join(grammarDir, "tree-sitter.wasm"))) {
|
|
33916
34318
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
33917
34319
|
}
|
|
33918
34320
|
for (const file3 of grammarFiles) {
|
|
33919
|
-
if (!
|
|
34321
|
+
if (!existsSync5(path15.join(grammarDir, file3))) {
|
|
33920
34322
|
missing.push(file3);
|
|
33921
34323
|
}
|
|
33922
34324
|
}
|
|
@@ -33934,8 +34336,8 @@ async function checkGrammarWasmFiles() {
|
|
|
33934
34336
|
};
|
|
33935
34337
|
}
|
|
33936
34338
|
async function checkCheckpointManifest(directory) {
|
|
33937
|
-
const manifestPath =
|
|
33938
|
-
if (!
|
|
34339
|
+
const manifestPath = path15.join(directory, ".swarm/checkpoints.json");
|
|
34340
|
+
if (!existsSync5(manifestPath)) {
|
|
33939
34341
|
return {
|
|
33940
34342
|
name: "Checkpoint Manifest",
|
|
33941
34343
|
status: "\u2705",
|
|
@@ -33943,7 +34345,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
33943
34345
|
};
|
|
33944
34346
|
}
|
|
33945
34347
|
try {
|
|
33946
|
-
const content =
|
|
34348
|
+
const content = readFileSync5(manifestPath, "utf-8");
|
|
33947
34349
|
const parsed = JSON.parse(content);
|
|
33948
34350
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
33949
34351
|
return {
|
|
@@ -33986,8 +34388,8 @@ async function checkCheckpointManifest(directory) {
|
|
|
33986
34388
|
}
|
|
33987
34389
|
}
|
|
33988
34390
|
async function checkEventStreamIntegrity(directory) {
|
|
33989
|
-
const eventsPath =
|
|
33990
|
-
if (!
|
|
34391
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34392
|
+
if (!existsSync5(eventsPath)) {
|
|
33991
34393
|
return {
|
|
33992
34394
|
name: "Event Stream",
|
|
33993
34395
|
status: "\u2705",
|
|
@@ -33995,7 +34397,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
33995
34397
|
};
|
|
33996
34398
|
}
|
|
33997
34399
|
try {
|
|
33998
|
-
const content =
|
|
34400
|
+
const content = readFileSync5(eventsPath, "utf-8");
|
|
33999
34401
|
const lines = content.split(`
|
|
34000
34402
|
`).filter((line) => line.trim() !== "");
|
|
34001
34403
|
let malformedCount = 0;
|
|
@@ -34027,8 +34429,8 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
34027
34429
|
}
|
|
34028
34430
|
}
|
|
34029
34431
|
async function checkSteeringDirectives(directory) {
|
|
34030
|
-
const eventsPath =
|
|
34031
|
-
if (!
|
|
34432
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34433
|
+
if (!existsSync5(eventsPath)) {
|
|
34032
34434
|
return {
|
|
34033
34435
|
name: "Steering Directives",
|
|
34034
34436
|
status: "\u2705",
|
|
@@ -34036,7 +34438,7 @@ async function checkSteeringDirectives(directory) {
|
|
|
34036
34438
|
};
|
|
34037
34439
|
}
|
|
34038
34440
|
try {
|
|
34039
|
-
const content =
|
|
34441
|
+
const content = readFileSync5(eventsPath, "utf-8");
|
|
34040
34442
|
const lines = content.split(`
|
|
34041
34443
|
`).filter((line) => line.trim() !== "");
|
|
34042
34444
|
const directivesIssued = [];
|
|
@@ -34083,8 +34485,8 @@ async function checkCurator(directory) {
|
|
|
34083
34485
|
detail: "Disabled (enable via curator.enabled)"
|
|
34084
34486
|
};
|
|
34085
34487
|
}
|
|
34086
|
-
const summaryPath =
|
|
34087
|
-
if (!
|
|
34488
|
+
const summaryPath = path15.join(directory, ".swarm/curator-summary.json");
|
|
34489
|
+
if (!existsSync5(summaryPath)) {
|
|
34088
34490
|
return {
|
|
34089
34491
|
name: "Curator",
|
|
34090
34492
|
status: "\u2705",
|
|
@@ -34092,7 +34494,7 @@ async function checkCurator(directory) {
|
|
|
34092
34494
|
};
|
|
34093
34495
|
}
|
|
34094
34496
|
try {
|
|
34095
|
-
const content =
|
|
34497
|
+
const content = readFileSync5(summaryPath, "utf-8");
|
|
34096
34498
|
const parsed = JSON.parse(content);
|
|
34097
34499
|
if (typeof parsed.schema_version !== "number" || parsed.schema_version !== 1) {
|
|
34098
34500
|
return {
|
|
@@ -34522,8 +34924,8 @@ async function handleExportCommand(directory, _args) {
|
|
|
34522
34924
|
}
|
|
34523
34925
|
// src/commands/handoff.ts
|
|
34524
34926
|
init_utils2();
|
|
34525
|
-
import
|
|
34526
|
-
import { renameSync as
|
|
34927
|
+
import crypto4 from "crypto";
|
|
34928
|
+
import { renameSync as renameSync5 } from "fs";
|
|
34527
34929
|
|
|
34528
34930
|
// src/services/handoff-service.ts
|
|
34529
34931
|
init_utils2();
|
|
@@ -34831,9 +35233,9 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
34831
35233
|
const handoffData = await getHandoffData(directory);
|
|
34832
35234
|
const markdown = formatHandoffMarkdown(handoffData);
|
|
34833
35235
|
const resolvedPath = validateSwarmPath(directory, "handoff.md");
|
|
34834
|
-
const tempPath = `${resolvedPath}.tmp.${
|
|
35236
|
+
const tempPath = `${resolvedPath}.tmp.${crypto4.randomUUID()}`;
|
|
34835
35237
|
await Bun.write(tempPath, markdown);
|
|
34836
|
-
|
|
35238
|
+
renameSync5(tempPath, resolvedPath);
|
|
34837
35239
|
await writeSnapshot(directory, swarmState);
|
|
34838
35240
|
await flushPendingSnapshot(directory);
|
|
34839
35241
|
return `## Handoff Brief Written
|
|
@@ -34978,14 +35380,14 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
34978
35380
|
}
|
|
34979
35381
|
// src/hooks/knowledge-migrator.ts
|
|
34980
35382
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
34981
|
-
import { existsSync as
|
|
35383
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
34982
35384
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
34983
|
-
import * as
|
|
35385
|
+
import * as path17 from "path";
|
|
34984
35386
|
async function migrateContextToKnowledge(directory, config3) {
|
|
34985
|
-
const sentinelPath =
|
|
34986
|
-
const contextPath =
|
|
35387
|
+
const sentinelPath = path17.join(directory, ".swarm", ".knowledge-migrated");
|
|
35388
|
+
const contextPath = path17.join(directory, ".swarm", "context.md");
|
|
34987
35389
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
34988
|
-
if (
|
|
35390
|
+
if (existsSync7(sentinelPath)) {
|
|
34989
35391
|
return {
|
|
34990
35392
|
migrated: false,
|
|
34991
35393
|
entriesMigrated: 0,
|
|
@@ -34994,7 +35396,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
34994
35396
|
skippedReason: "sentinel-exists"
|
|
34995
35397
|
};
|
|
34996
35398
|
}
|
|
34997
|
-
if (!
|
|
35399
|
+
if (!existsSync7(contextPath)) {
|
|
34998
35400
|
return {
|
|
34999
35401
|
migrated: false,
|
|
35000
35402
|
entriesMigrated: 0,
|
|
@@ -35179,16 +35581,16 @@ function truncateLesson(text) {
|
|
|
35179
35581
|
return `${text.slice(0, 277)}...`;
|
|
35180
35582
|
}
|
|
35181
35583
|
function inferProjectName(directory) {
|
|
35182
|
-
const packageJsonPath =
|
|
35183
|
-
if (
|
|
35584
|
+
const packageJsonPath = path17.join(directory, "package.json");
|
|
35585
|
+
if (existsSync7(packageJsonPath)) {
|
|
35184
35586
|
try {
|
|
35185
|
-
const pkg = JSON.parse(
|
|
35587
|
+
const pkg = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
35186
35588
|
if (pkg.name && typeof pkg.name === "string") {
|
|
35187
35589
|
return pkg.name;
|
|
35188
35590
|
}
|
|
35189
35591
|
} catch {}
|
|
35190
35592
|
}
|
|
35191
|
-
return
|
|
35593
|
+
return path17.basename(directory);
|
|
35192
35594
|
}
|
|
35193
35595
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
35194
35596
|
const sentinel = {
|
|
@@ -35200,7 +35602,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
35200
35602
|
schema_version: 1,
|
|
35201
35603
|
migration_tool: "knowledge-migrator.ts"
|
|
35202
35604
|
};
|
|
35203
|
-
await mkdir3(
|
|
35605
|
+
await mkdir3(path17.dirname(sentinelPath), { recursive: true });
|
|
35204
35606
|
await writeFile3(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
35205
35607
|
}
|
|
35206
35608
|
|
|
@@ -35436,20 +35838,20 @@ async function handlePlanCommand(directory, args) {
|
|
|
35436
35838
|
// src/services/preflight-service.ts
|
|
35437
35839
|
init_manager();
|
|
35438
35840
|
init_manager2();
|
|
35439
|
-
import * as
|
|
35440
|
-
import * as
|
|
35841
|
+
import * as fs13 from "fs";
|
|
35842
|
+
import * as path23 from "path";
|
|
35441
35843
|
|
|
35442
35844
|
// src/tools/lint.ts
|
|
35443
|
-
import * as
|
|
35444
|
-
import * as
|
|
35845
|
+
import * as fs9 from "fs";
|
|
35846
|
+
import * as path19 from "path";
|
|
35445
35847
|
|
|
35446
35848
|
// src/build/discovery.ts
|
|
35447
|
-
import * as
|
|
35448
|
-
import * as
|
|
35849
|
+
import * as fs8 from "fs";
|
|
35850
|
+
import * as path18 from "path";
|
|
35449
35851
|
|
|
35450
35852
|
// src/lang/detector.ts
|
|
35451
35853
|
import { access, readdir as readdir2 } from "fs/promises";
|
|
35452
|
-
import { extname as extname2, join as
|
|
35854
|
+
import { extname as extname2, join as join13 } from "path";
|
|
35453
35855
|
|
|
35454
35856
|
// src/lang/profiles.ts
|
|
35455
35857
|
class LanguageRegistry {
|
|
@@ -36374,7 +36776,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
36374
36776
|
if (detectFile.includes("*") || detectFile.includes("?"))
|
|
36375
36777
|
continue;
|
|
36376
36778
|
try {
|
|
36377
|
-
await access(
|
|
36779
|
+
await access(join13(dir, detectFile));
|
|
36378
36780
|
detected.add(profile.id);
|
|
36379
36781
|
break;
|
|
36380
36782
|
} catch {}
|
|
@@ -36395,7 +36797,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
36395
36797
|
const topEntries = await readdir2(projectDir, { withFileTypes: true });
|
|
36396
36798
|
for (const entry of topEntries) {
|
|
36397
36799
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
36398
|
-
await scanDir(
|
|
36800
|
+
await scanDir(join13(projectDir, entry.name));
|
|
36399
36801
|
}
|
|
36400
36802
|
}
|
|
36401
36803
|
} catch {}
|
|
@@ -36538,16 +36940,16 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36538
36940
|
if (pattern.includes("*")) {
|
|
36539
36941
|
const dir = workingDir;
|
|
36540
36942
|
try {
|
|
36541
|
-
const files =
|
|
36943
|
+
const files = fs8.readdirSync(dir);
|
|
36542
36944
|
const regex = simpleGlobToRegex(pattern);
|
|
36543
36945
|
const matches = files.filter((f) => regex.test(f));
|
|
36544
36946
|
if (matches.length > 0) {
|
|
36545
|
-
return
|
|
36947
|
+
return path18.join(dir, matches[0]);
|
|
36546
36948
|
}
|
|
36547
36949
|
} catch {}
|
|
36548
36950
|
} else {
|
|
36549
|
-
const filePath =
|
|
36550
|
-
if (
|
|
36951
|
+
const filePath = path18.join(workingDir, pattern);
|
|
36952
|
+
if (fs8.existsSync(filePath)) {
|
|
36551
36953
|
return filePath;
|
|
36552
36954
|
}
|
|
36553
36955
|
}
|
|
@@ -36555,12 +36957,12 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36555
36957
|
return null;
|
|
36556
36958
|
}
|
|
36557
36959
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
36558
|
-
const packageJsonPath =
|
|
36559
|
-
if (!
|
|
36960
|
+
const packageJsonPath = path18.join(workingDir, "package.json");
|
|
36961
|
+
if (!fs8.existsSync(packageJsonPath)) {
|
|
36560
36962
|
return [];
|
|
36561
36963
|
}
|
|
36562
36964
|
try {
|
|
36563
|
-
const content =
|
|
36965
|
+
const content = fs8.readFileSync(packageJsonPath, "utf-8");
|
|
36564
36966
|
const pkg = JSON.parse(content);
|
|
36565
36967
|
if (!pkg.scripts || typeof pkg.scripts !== "object") {
|
|
36566
36968
|
return [];
|
|
@@ -36596,8 +36998,8 @@ function findAllBuildFiles(workingDir) {
|
|
|
36596
36998
|
const regex = simpleGlobToRegex(pattern);
|
|
36597
36999
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
36598
37000
|
} else {
|
|
36599
|
-
const filePath =
|
|
36600
|
-
if (
|
|
37001
|
+
const filePath = path18.join(workingDir, pattern);
|
|
37002
|
+
if (fs8.existsSync(filePath)) {
|
|
36601
37003
|
allBuildFiles.add(filePath);
|
|
36602
37004
|
}
|
|
36603
37005
|
}
|
|
@@ -36607,9 +37009,9 @@ function findAllBuildFiles(workingDir) {
|
|
|
36607
37009
|
}
|
|
36608
37010
|
function findFilesRecursive(dir, regex, results) {
|
|
36609
37011
|
try {
|
|
36610
|
-
const entries =
|
|
37012
|
+
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
36611
37013
|
for (const entry of entries) {
|
|
36612
|
-
const fullPath =
|
|
37014
|
+
const fullPath = path18.join(dir, entry.name);
|
|
36613
37015
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
36614
37016
|
findFilesRecursive(fullPath, regex, results);
|
|
36615
37017
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -36632,8 +37034,8 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
|
|
|
36632
37034
|
let foundCommand = false;
|
|
36633
37035
|
for (const cmd of sortedCommands) {
|
|
36634
37036
|
if (cmd.detectFile) {
|
|
36635
|
-
const detectFilePath =
|
|
36636
|
-
if (!
|
|
37037
|
+
const detectFilePath = path18.join(workingDir, cmd.detectFile);
|
|
37038
|
+
if (!fs8.existsSync(detectFilePath)) {
|
|
36637
37039
|
continue;
|
|
36638
37040
|
}
|
|
36639
37041
|
}
|
|
@@ -36780,9 +37182,9 @@ function validateArgs(args) {
|
|
|
36780
37182
|
}
|
|
36781
37183
|
function getLinterCommand(linter, mode, projectDir) {
|
|
36782
37184
|
const isWindows = process.platform === "win32";
|
|
36783
|
-
const binDir =
|
|
36784
|
-
const biomeBin = isWindows ?
|
|
36785
|
-
const eslintBin = isWindows ?
|
|
37185
|
+
const binDir = path19.join(projectDir, "node_modules", ".bin");
|
|
37186
|
+
const biomeBin = isWindows ? path19.join(binDir, "biome.EXE") : path19.join(binDir, "biome");
|
|
37187
|
+
const eslintBin = isWindows ? path19.join(binDir, "eslint.cmd") : path19.join(binDir, "eslint");
|
|
36786
37188
|
switch (linter) {
|
|
36787
37189
|
case "biome":
|
|
36788
37190
|
if (mode === "fix") {
|
|
@@ -36798,7 +37200,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
36798
37200
|
}
|
|
36799
37201
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
36800
37202
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
36801
|
-
const gradlew =
|
|
37203
|
+
const gradlew = fs9.existsSync(path19.join(cwd, gradlewName)) ? path19.join(cwd, gradlewName) : null;
|
|
36802
37204
|
switch (linter) {
|
|
36803
37205
|
case "ruff":
|
|
36804
37206
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -36832,12 +37234,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
36832
37234
|
}
|
|
36833
37235
|
}
|
|
36834
37236
|
function detectRuff(cwd) {
|
|
36835
|
-
if (
|
|
37237
|
+
if (fs9.existsSync(path19.join(cwd, "ruff.toml")))
|
|
36836
37238
|
return isCommandAvailable("ruff");
|
|
36837
37239
|
try {
|
|
36838
|
-
const pyproject =
|
|
36839
|
-
if (
|
|
36840
|
-
const content =
|
|
37240
|
+
const pyproject = path19.join(cwd, "pyproject.toml");
|
|
37241
|
+
if (fs9.existsSync(pyproject)) {
|
|
37242
|
+
const content = fs9.readFileSync(pyproject, "utf-8");
|
|
36841
37243
|
if (content.includes("[tool.ruff]"))
|
|
36842
37244
|
return isCommandAvailable("ruff");
|
|
36843
37245
|
}
|
|
@@ -36845,21 +37247,21 @@ function detectRuff(cwd) {
|
|
|
36845
37247
|
return false;
|
|
36846
37248
|
}
|
|
36847
37249
|
function detectClippy(cwd) {
|
|
36848
|
-
return
|
|
37250
|
+
return fs9.existsSync(path19.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
36849
37251
|
}
|
|
36850
37252
|
function detectGolangciLint(cwd) {
|
|
36851
|
-
return
|
|
37253
|
+
return fs9.existsSync(path19.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
36852
37254
|
}
|
|
36853
37255
|
function detectCheckstyle(cwd) {
|
|
36854
|
-
const hasMaven =
|
|
36855
|
-
const hasGradle =
|
|
36856
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (
|
|
37256
|
+
const hasMaven = fs9.existsSync(path19.join(cwd, "pom.xml"));
|
|
37257
|
+
const hasGradle = fs9.existsSync(path19.join(cwd, "build.gradle")) || fs9.existsSync(path19.join(cwd, "build.gradle.kts"));
|
|
37258
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs9.existsSync(path19.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
36857
37259
|
return (hasMaven || hasGradle) && hasBinary;
|
|
36858
37260
|
}
|
|
36859
37261
|
function detectKtlint(cwd) {
|
|
36860
|
-
const hasKotlin =
|
|
37262
|
+
const hasKotlin = fs9.existsSync(path19.join(cwd, "build.gradle.kts")) || fs9.existsSync(path19.join(cwd, "build.gradle")) || (() => {
|
|
36861
37263
|
try {
|
|
36862
|
-
return
|
|
37264
|
+
return fs9.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
36863
37265
|
} catch {
|
|
36864
37266
|
return false;
|
|
36865
37267
|
}
|
|
@@ -36868,7 +37270,7 @@ function detectKtlint(cwd) {
|
|
|
36868
37270
|
}
|
|
36869
37271
|
function detectDotnetFormat(cwd) {
|
|
36870
37272
|
try {
|
|
36871
|
-
const files =
|
|
37273
|
+
const files = fs9.readdirSync(cwd);
|
|
36872
37274
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
36873
37275
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
36874
37276
|
} catch {
|
|
@@ -36876,14 +37278,14 @@ function detectDotnetFormat(cwd) {
|
|
|
36876
37278
|
}
|
|
36877
37279
|
}
|
|
36878
37280
|
function detectCppcheck(cwd) {
|
|
36879
|
-
if (
|
|
37281
|
+
if (fs9.existsSync(path19.join(cwd, "CMakeLists.txt"))) {
|
|
36880
37282
|
return isCommandAvailable("cppcheck");
|
|
36881
37283
|
}
|
|
36882
37284
|
try {
|
|
36883
|
-
const dirsToCheck = [cwd,
|
|
37285
|
+
const dirsToCheck = [cwd, path19.join(cwd, "src")];
|
|
36884
37286
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
36885
37287
|
try {
|
|
36886
|
-
return
|
|
37288
|
+
return fs9.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
36887
37289
|
} catch {
|
|
36888
37290
|
return false;
|
|
36889
37291
|
}
|
|
@@ -36894,13 +37296,13 @@ function detectCppcheck(cwd) {
|
|
|
36894
37296
|
}
|
|
36895
37297
|
}
|
|
36896
37298
|
function detectSwiftlint(cwd) {
|
|
36897
|
-
return
|
|
37299
|
+
return fs9.existsSync(path19.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
36898
37300
|
}
|
|
36899
37301
|
function detectDartAnalyze(cwd) {
|
|
36900
|
-
return
|
|
37302
|
+
return fs9.existsSync(path19.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
36901
37303
|
}
|
|
36902
37304
|
function detectRubocop(cwd) {
|
|
36903
|
-
return (
|
|
37305
|
+
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"));
|
|
36904
37306
|
}
|
|
36905
37307
|
function detectAdditionalLinter(cwd) {
|
|
36906
37308
|
if (detectRuff(cwd))
|
|
@@ -36929,12 +37331,12 @@ async function detectAvailableLinter(directory) {
|
|
|
36929
37331
|
const _DETECT_TIMEOUT = 2000;
|
|
36930
37332
|
if (!directory)
|
|
36931
37333
|
return null;
|
|
36932
|
-
if (!
|
|
37334
|
+
if (!fs9.existsSync(directory))
|
|
36933
37335
|
return null;
|
|
36934
37336
|
const projectDir = directory;
|
|
36935
37337
|
const isWindows = process.platform === "win32";
|
|
36936
|
-
const biomeBin = isWindows ?
|
|
36937
|
-
const eslintBin = isWindows ?
|
|
37338
|
+
const biomeBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "biome.EXE") : path19.join(projectDir, "node_modules", ".bin", "biome");
|
|
37339
|
+
const eslintBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path19.join(projectDir, "node_modules", ".bin", "eslint");
|
|
36938
37340
|
return _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
36939
37341
|
}
|
|
36940
37342
|
async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
@@ -36949,7 +37351,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36949
37351
|
const result = await Promise.race([biomeExit, timeout]);
|
|
36950
37352
|
if (result === "timeout") {
|
|
36951
37353
|
biomeProc.kill();
|
|
36952
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
37354
|
+
} else if (biomeProc.exitCode === 0 && fs9.existsSync(biomeBin)) {
|
|
36953
37355
|
return "biome";
|
|
36954
37356
|
}
|
|
36955
37357
|
} catch {}
|
|
@@ -36963,7 +37365,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36963
37365
|
const result = await Promise.race([eslintExit, timeout]);
|
|
36964
37366
|
if (result === "timeout") {
|
|
36965
37367
|
eslintProc.kill();
|
|
36966
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
37368
|
+
} else if (eslintProc.exitCode === 0 && fs9.existsSync(eslintBin)) {
|
|
36967
37369
|
return "eslint";
|
|
36968
37370
|
}
|
|
36969
37371
|
} catch {}
|
|
@@ -37133,8 +37535,8 @@ For Rust: rustup component add clippy`
|
|
|
37133
37535
|
});
|
|
37134
37536
|
|
|
37135
37537
|
// src/tools/secretscan.ts
|
|
37136
|
-
import * as
|
|
37137
|
-
import * as
|
|
37538
|
+
import * as fs10 from "fs";
|
|
37539
|
+
import * as path20 from "path";
|
|
37138
37540
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
37139
37541
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
37140
37542
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -37361,11 +37763,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
37361
37763
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
37362
37764
|
}
|
|
37363
37765
|
function loadSecretScanIgnore(scanDir) {
|
|
37364
|
-
const ignorePath =
|
|
37766
|
+
const ignorePath = path20.join(scanDir, ".secretscanignore");
|
|
37365
37767
|
try {
|
|
37366
|
-
if (!
|
|
37768
|
+
if (!fs10.existsSync(ignorePath))
|
|
37367
37769
|
return [];
|
|
37368
|
-
const content =
|
|
37770
|
+
const content = fs10.readFileSync(ignorePath, "utf8");
|
|
37369
37771
|
const patterns = [];
|
|
37370
37772
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
37371
37773
|
const line = rawLine.trim();
|
|
@@ -37384,7 +37786,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
37384
37786
|
if (exactNames.has(entry))
|
|
37385
37787
|
return true;
|
|
37386
37788
|
for (const pattern of globPatterns) {
|
|
37387
|
-
if (
|
|
37789
|
+
if (path20.matchesGlob(relPath, pattern))
|
|
37388
37790
|
return true;
|
|
37389
37791
|
}
|
|
37390
37792
|
return false;
|
|
@@ -37405,7 +37807,7 @@ function validateDirectoryInput(dir) {
|
|
|
37405
37807
|
return null;
|
|
37406
37808
|
}
|
|
37407
37809
|
function isBinaryFile(filePath, buffer) {
|
|
37408
|
-
const ext =
|
|
37810
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
37409
37811
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37410
37812
|
return true;
|
|
37411
37813
|
}
|
|
@@ -37480,11 +37882,11 @@ function createRedactedContext(line, findings) {
|
|
|
37480
37882
|
result += line.slice(lastEnd);
|
|
37481
37883
|
return result;
|
|
37482
37884
|
}
|
|
37483
|
-
var O_NOFOLLOW = process.platform !== "win32" ?
|
|
37885
|
+
var O_NOFOLLOW = process.platform !== "win32" ? fs10.constants.O_NOFOLLOW : undefined;
|
|
37484
37886
|
function scanFileForSecrets(filePath) {
|
|
37485
37887
|
const findings = [];
|
|
37486
37888
|
try {
|
|
37487
|
-
const lstat =
|
|
37889
|
+
const lstat = fs10.lstatSync(filePath);
|
|
37488
37890
|
if (lstat.isSymbolicLink()) {
|
|
37489
37891
|
return findings;
|
|
37490
37892
|
}
|
|
@@ -37493,14 +37895,14 @@ function scanFileForSecrets(filePath) {
|
|
|
37493
37895
|
}
|
|
37494
37896
|
let buffer;
|
|
37495
37897
|
if (O_NOFOLLOW !== undefined) {
|
|
37496
|
-
const fd =
|
|
37898
|
+
const fd = fs10.openSync(filePath, "r", O_NOFOLLOW);
|
|
37497
37899
|
try {
|
|
37498
|
-
buffer =
|
|
37900
|
+
buffer = fs10.readFileSync(fd);
|
|
37499
37901
|
} finally {
|
|
37500
|
-
|
|
37902
|
+
fs10.closeSync(fd);
|
|
37501
37903
|
}
|
|
37502
37904
|
} else {
|
|
37503
|
-
buffer =
|
|
37905
|
+
buffer = fs10.readFileSync(filePath);
|
|
37504
37906
|
}
|
|
37505
37907
|
if (isBinaryFile(filePath, buffer)) {
|
|
37506
37908
|
return findings;
|
|
@@ -37542,9 +37944,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
37542
37944
|
return false;
|
|
37543
37945
|
}
|
|
37544
37946
|
function isPathWithinScope(realPath, scanDir) {
|
|
37545
|
-
const resolvedScanDir =
|
|
37546
|
-
const resolvedRealPath =
|
|
37547
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
37947
|
+
const resolvedScanDir = path20.resolve(scanDir);
|
|
37948
|
+
const resolvedRealPath = path20.resolve(realPath);
|
|
37949
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path20.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
37548
37950
|
}
|
|
37549
37951
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
37550
37952
|
skippedDirs: 0,
|
|
@@ -37555,7 +37957,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37555
37957
|
const files = [];
|
|
37556
37958
|
let entries;
|
|
37557
37959
|
try {
|
|
37558
|
-
entries =
|
|
37960
|
+
entries = fs10.readdirSync(dir);
|
|
37559
37961
|
} catch {
|
|
37560
37962
|
stats.fileErrors++;
|
|
37561
37963
|
return files;
|
|
@@ -37570,15 +37972,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37570
37972
|
return a.localeCompare(b);
|
|
37571
37973
|
});
|
|
37572
37974
|
for (const entry of entries) {
|
|
37573
|
-
const fullPath =
|
|
37574
|
-
const relPath =
|
|
37975
|
+
const fullPath = path20.join(dir, entry);
|
|
37976
|
+
const relPath = path20.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
37575
37977
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
37576
37978
|
stats.skippedDirs++;
|
|
37577
37979
|
continue;
|
|
37578
37980
|
}
|
|
37579
37981
|
let lstat;
|
|
37580
37982
|
try {
|
|
37581
|
-
lstat =
|
|
37983
|
+
lstat = fs10.lstatSync(fullPath);
|
|
37582
37984
|
} catch {
|
|
37583
37985
|
stats.fileErrors++;
|
|
37584
37986
|
continue;
|
|
@@ -37590,7 +37992,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37590
37992
|
if (lstat.isDirectory()) {
|
|
37591
37993
|
let realPath;
|
|
37592
37994
|
try {
|
|
37593
|
-
realPath =
|
|
37995
|
+
realPath = fs10.realpathSync(fullPath);
|
|
37594
37996
|
} catch {
|
|
37595
37997
|
stats.fileErrors++;
|
|
37596
37998
|
continue;
|
|
@@ -37606,7 +38008,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37606
38008
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
37607
38009
|
files.push(...subFiles);
|
|
37608
38010
|
} else if (lstat.isFile()) {
|
|
37609
|
-
const ext =
|
|
38011
|
+
const ext = path20.extname(fullPath).toLowerCase();
|
|
37610
38012
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37611
38013
|
files.push(fullPath);
|
|
37612
38014
|
} else {
|
|
@@ -37672,8 +38074,8 @@ var secretscan = createSwarmTool({
|
|
|
37672
38074
|
}
|
|
37673
38075
|
}
|
|
37674
38076
|
try {
|
|
37675
|
-
const scanDir =
|
|
37676
|
-
if (!
|
|
38077
|
+
const scanDir = path20.resolve(directory);
|
|
38078
|
+
if (!fs10.existsSync(scanDir)) {
|
|
37677
38079
|
const errorResult = {
|
|
37678
38080
|
error: "directory not found",
|
|
37679
38081
|
scan_dir: directory,
|
|
@@ -37684,7 +38086,7 @@ var secretscan = createSwarmTool({
|
|
|
37684
38086
|
};
|
|
37685
38087
|
return JSON.stringify(errorResult, null, 2);
|
|
37686
38088
|
}
|
|
37687
|
-
const dirStat =
|
|
38089
|
+
const dirStat = fs10.statSync(scanDir);
|
|
37688
38090
|
if (!dirStat.isDirectory()) {
|
|
37689
38091
|
const errorResult = {
|
|
37690
38092
|
error: "target must be a directory, not a file",
|
|
@@ -37735,7 +38137,7 @@ var secretscan = createSwarmTool({
|
|
|
37735
38137
|
break;
|
|
37736
38138
|
const fileFindings = scanFileForSecrets(filePath);
|
|
37737
38139
|
try {
|
|
37738
|
-
const stat2 =
|
|
38140
|
+
const stat2 = fs10.statSync(filePath);
|
|
37739
38141
|
if (stat2.size > MAX_FILE_SIZE_BYTES) {
|
|
37740
38142
|
skippedFiles++;
|
|
37741
38143
|
continue;
|
|
@@ -37822,12 +38224,12 @@ async function runSecretscan(directory) {
|
|
|
37822
38224
|
}
|
|
37823
38225
|
|
|
37824
38226
|
// src/tools/test-runner.ts
|
|
37825
|
-
import * as
|
|
37826
|
-
import * as
|
|
38227
|
+
import * as fs12 from "fs";
|
|
38228
|
+
import * as path22 from "path";
|
|
37827
38229
|
|
|
37828
38230
|
// src/tools/resolve-working-directory.ts
|
|
37829
|
-
import * as
|
|
37830
|
-
import * as
|
|
38231
|
+
import * as fs11 from "fs";
|
|
38232
|
+
import * as path21 from "path";
|
|
37831
38233
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
37832
38234
|
if (workingDirectory == null || workingDirectory === "") {
|
|
37833
38235
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -37847,17 +38249,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
37847
38249
|
};
|
|
37848
38250
|
}
|
|
37849
38251
|
}
|
|
37850
|
-
const normalizedDir =
|
|
37851
|
-
const pathParts = normalizedDir.split(
|
|
38252
|
+
const normalizedDir = path21.normalize(workingDirectory);
|
|
38253
|
+
const pathParts = normalizedDir.split(path21.sep);
|
|
37852
38254
|
if (pathParts.includes("..")) {
|
|
37853
38255
|
return {
|
|
37854
38256
|
success: false,
|
|
37855
38257
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
37856
38258
|
};
|
|
37857
38259
|
}
|
|
37858
|
-
const resolvedDir =
|
|
38260
|
+
const resolvedDir = path21.resolve(normalizedDir);
|
|
37859
38261
|
try {
|
|
37860
|
-
const realPath =
|
|
38262
|
+
const realPath = fs11.realpathSync(resolvedDir);
|
|
37861
38263
|
return { success: true, directory: realPath };
|
|
37862
38264
|
} catch {
|
|
37863
38265
|
return {
|
|
@@ -37938,19 +38340,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
37938
38340
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
37939
38341
|
}
|
|
37940
38342
|
function detectGoTest(cwd) {
|
|
37941
|
-
return
|
|
38343
|
+
return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
37942
38344
|
}
|
|
37943
38345
|
function detectJavaMaven(cwd) {
|
|
37944
|
-
return
|
|
38346
|
+
return fs12.existsSync(path22.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
37945
38347
|
}
|
|
37946
38348
|
function detectGradle(cwd) {
|
|
37947
|
-
const hasBuildFile =
|
|
37948
|
-
const hasGradlew =
|
|
38349
|
+
const hasBuildFile = fs12.existsSync(path22.join(cwd, "build.gradle")) || fs12.existsSync(path22.join(cwd, "build.gradle.kts"));
|
|
38350
|
+
const hasGradlew = fs12.existsSync(path22.join(cwd, "gradlew")) || fs12.existsSync(path22.join(cwd, "gradlew.bat"));
|
|
37949
38351
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
37950
38352
|
}
|
|
37951
38353
|
function detectDotnetTest(cwd) {
|
|
37952
38354
|
try {
|
|
37953
|
-
const files =
|
|
38355
|
+
const files = fs12.readdirSync(cwd);
|
|
37954
38356
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
37955
38357
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
37956
38358
|
} catch {
|
|
@@ -37958,32 +38360,32 @@ function detectDotnetTest(cwd) {
|
|
|
37958
38360
|
}
|
|
37959
38361
|
}
|
|
37960
38362
|
function detectCTest(cwd) {
|
|
37961
|
-
const hasSource =
|
|
37962
|
-
const hasBuildCache =
|
|
38363
|
+
const hasSource = fs12.existsSync(path22.join(cwd, "CMakeLists.txt"));
|
|
38364
|
+
const hasBuildCache = fs12.existsSync(path22.join(cwd, "CMakeCache.txt")) || fs12.existsSync(path22.join(cwd, "build", "CMakeCache.txt"));
|
|
37963
38365
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
37964
38366
|
}
|
|
37965
38367
|
function detectSwiftTest(cwd) {
|
|
37966
|
-
return
|
|
38368
|
+
return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
37967
38369
|
}
|
|
37968
38370
|
function detectDartTest(cwd) {
|
|
37969
|
-
return
|
|
38371
|
+
return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
37970
38372
|
}
|
|
37971
38373
|
function detectRSpec(cwd) {
|
|
37972
|
-
const hasRSpecFile =
|
|
37973
|
-
const hasGemfile =
|
|
37974
|
-
const hasSpecDir =
|
|
38374
|
+
const hasRSpecFile = fs12.existsSync(path22.join(cwd, ".rspec"));
|
|
38375
|
+
const hasGemfile = fs12.existsSync(path22.join(cwd, "Gemfile"));
|
|
38376
|
+
const hasSpecDir = fs12.existsSync(path22.join(cwd, "spec"));
|
|
37975
38377
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
37976
38378
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
37977
38379
|
}
|
|
37978
38380
|
function detectMinitest(cwd) {
|
|
37979
|
-
return
|
|
38381
|
+
return fs12.existsSync(path22.join(cwd, "test")) && (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
37980
38382
|
}
|
|
37981
38383
|
async function detectTestFramework(cwd) {
|
|
37982
38384
|
const baseDir = cwd;
|
|
37983
38385
|
try {
|
|
37984
|
-
const packageJsonPath =
|
|
37985
|
-
if (
|
|
37986
|
-
const content =
|
|
38386
|
+
const packageJsonPath = path22.join(baseDir, "package.json");
|
|
38387
|
+
if (fs12.existsSync(packageJsonPath)) {
|
|
38388
|
+
const content = fs12.readFileSync(packageJsonPath, "utf-8");
|
|
37987
38389
|
const pkg = JSON.parse(content);
|
|
37988
38390
|
const _deps = pkg.dependencies || {};
|
|
37989
38391
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -38002,38 +38404,38 @@ async function detectTestFramework(cwd) {
|
|
|
38002
38404
|
return "jest";
|
|
38003
38405
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
38004
38406
|
return "mocha";
|
|
38005
|
-
if (
|
|
38407
|
+
if (fs12.existsSync(path22.join(baseDir, "bun.lockb")) || fs12.existsSync(path22.join(baseDir, "bun.lock"))) {
|
|
38006
38408
|
if (scripts.test?.includes("bun"))
|
|
38007
38409
|
return "bun";
|
|
38008
38410
|
}
|
|
38009
38411
|
}
|
|
38010
38412
|
} catch {}
|
|
38011
38413
|
try {
|
|
38012
|
-
const pyprojectTomlPath =
|
|
38013
|
-
const setupCfgPath =
|
|
38014
|
-
const requirementsTxtPath =
|
|
38015
|
-
if (
|
|
38016
|
-
const content =
|
|
38414
|
+
const pyprojectTomlPath = path22.join(baseDir, "pyproject.toml");
|
|
38415
|
+
const setupCfgPath = path22.join(baseDir, "setup.cfg");
|
|
38416
|
+
const requirementsTxtPath = path22.join(baseDir, "requirements.txt");
|
|
38417
|
+
if (fs12.existsSync(pyprojectTomlPath)) {
|
|
38418
|
+
const content = fs12.readFileSync(pyprojectTomlPath, "utf-8");
|
|
38017
38419
|
if (content.includes("[tool.pytest"))
|
|
38018
38420
|
return "pytest";
|
|
38019
38421
|
if (content.includes("pytest"))
|
|
38020
38422
|
return "pytest";
|
|
38021
38423
|
}
|
|
38022
|
-
if (
|
|
38023
|
-
const content =
|
|
38424
|
+
if (fs12.existsSync(setupCfgPath)) {
|
|
38425
|
+
const content = fs12.readFileSync(setupCfgPath, "utf-8");
|
|
38024
38426
|
if (content.includes("[pytest]"))
|
|
38025
38427
|
return "pytest";
|
|
38026
38428
|
}
|
|
38027
|
-
if (
|
|
38028
|
-
const content =
|
|
38429
|
+
if (fs12.existsSync(requirementsTxtPath)) {
|
|
38430
|
+
const content = fs12.readFileSync(requirementsTxtPath, "utf-8");
|
|
38029
38431
|
if (content.includes("pytest"))
|
|
38030
38432
|
return "pytest";
|
|
38031
38433
|
}
|
|
38032
38434
|
} catch {}
|
|
38033
38435
|
try {
|
|
38034
|
-
const cargoTomlPath =
|
|
38035
|
-
if (
|
|
38036
|
-
const content =
|
|
38436
|
+
const cargoTomlPath = path22.join(baseDir, "Cargo.toml");
|
|
38437
|
+
if (fs12.existsSync(cargoTomlPath)) {
|
|
38438
|
+
const content = fs12.readFileSync(cargoTomlPath, "utf-8");
|
|
38037
38439
|
if (content.includes("[dev-dependencies]")) {
|
|
38038
38440
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
38039
38441
|
return "cargo";
|
|
@@ -38042,10 +38444,10 @@ async function detectTestFramework(cwd) {
|
|
|
38042
38444
|
}
|
|
38043
38445
|
} catch {}
|
|
38044
38446
|
try {
|
|
38045
|
-
const pesterConfigPath =
|
|
38046
|
-
const pesterConfigJsonPath =
|
|
38047
|
-
const pesterPs1Path =
|
|
38048
|
-
if (
|
|
38447
|
+
const pesterConfigPath = path22.join(baseDir, "pester.config.ps1");
|
|
38448
|
+
const pesterConfigJsonPath = path22.join(baseDir, "pester.config.ps1.json");
|
|
38449
|
+
const pesterPs1Path = path22.join(baseDir, "tests.ps1");
|
|
38450
|
+
if (fs12.existsSync(pesterConfigPath) || fs12.existsSync(pesterConfigJsonPath) || fs12.existsSync(pesterPs1Path)) {
|
|
38049
38451
|
return "pester";
|
|
38050
38452
|
}
|
|
38051
38453
|
} catch {}
|
|
@@ -38096,8 +38498,8 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38096
38498
|
const testFiles = [];
|
|
38097
38499
|
for (const file3 of sourceFiles) {
|
|
38098
38500
|
const normalizedPath = file3.replace(/\\/g, "/");
|
|
38099
|
-
const basename4 =
|
|
38100
|
-
const dirname9 =
|
|
38501
|
+
const basename4 = path22.basename(file3);
|
|
38502
|
+
const dirname9 = path22.dirname(file3);
|
|
38101
38503
|
if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
38102
38504
|
if (!testFiles.includes(file3)) {
|
|
38103
38505
|
testFiles.push(file3);
|
|
@@ -38106,16 +38508,16 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38106
38508
|
}
|
|
38107
38509
|
for (const _pattern of TEST_PATTERNS) {
|
|
38108
38510
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
38109
|
-
const ext =
|
|
38511
|
+
const ext = path22.extname(basename4);
|
|
38110
38512
|
const possibleTestFiles = [
|
|
38111
|
-
|
|
38112
|
-
|
|
38113
|
-
|
|
38114
|
-
|
|
38115
|
-
|
|
38513
|
+
path22.join(dirname9, `${nameWithoutExt}.spec${ext}`),
|
|
38514
|
+
path22.join(dirname9, `${nameWithoutExt}.test${ext}`),
|
|
38515
|
+
path22.join(dirname9, "__tests__", `${nameWithoutExt}${ext}`),
|
|
38516
|
+
path22.join(dirname9, "tests", `${nameWithoutExt}${ext}`),
|
|
38517
|
+
path22.join(dirname9, "test", `${nameWithoutExt}${ext}`)
|
|
38116
38518
|
];
|
|
38117
38519
|
for (const testFile of possibleTestFiles) {
|
|
38118
|
-
if (
|
|
38520
|
+
if (fs12.existsSync(testFile) && !testFiles.includes(testFile)) {
|
|
38119
38521
|
testFiles.push(testFile);
|
|
38120
38522
|
}
|
|
38121
38523
|
}
|
|
@@ -38131,8 +38533,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38131
38533
|
}
|
|
38132
38534
|
for (const testFile of candidateTestFiles) {
|
|
38133
38535
|
try {
|
|
38134
|
-
const content =
|
|
38135
|
-
const testDir =
|
|
38536
|
+
const content = fs12.readFileSync(testFile, "utf-8");
|
|
38537
|
+
const testDir = path22.dirname(testFile);
|
|
38136
38538
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
38137
38539
|
let match;
|
|
38138
38540
|
match = importRegex.exec(content);
|
|
@@ -38140,8 +38542,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38140
38542
|
const importPath = match[1];
|
|
38141
38543
|
let resolvedImport;
|
|
38142
38544
|
if (importPath.startsWith(".")) {
|
|
38143
|
-
resolvedImport =
|
|
38144
|
-
const existingExt =
|
|
38545
|
+
resolvedImport = path22.resolve(testDir, importPath);
|
|
38546
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38145
38547
|
if (!existingExt) {
|
|
38146
38548
|
for (const extToTry of [
|
|
38147
38549
|
".ts",
|
|
@@ -38152,7 +38554,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38152
38554
|
".cjs"
|
|
38153
38555
|
]) {
|
|
38154
38556
|
const withExt = resolvedImport + extToTry;
|
|
38155
|
-
if (sourceFiles.includes(withExt) ||
|
|
38557
|
+
if (sourceFiles.includes(withExt) || fs12.existsSync(withExt)) {
|
|
38156
38558
|
resolvedImport = withExt;
|
|
38157
38559
|
break;
|
|
38158
38560
|
}
|
|
@@ -38161,12 +38563,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38161
38563
|
} else {
|
|
38162
38564
|
continue;
|
|
38163
38565
|
}
|
|
38164
|
-
const importBasename =
|
|
38165
|
-
const importDir =
|
|
38566
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38567
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38166
38568
|
for (const sourceFile of sourceFiles) {
|
|
38167
|
-
const sourceDir =
|
|
38168
|
-
const sourceBasename =
|
|
38169
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38569
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38570
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38571
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38170
38572
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38171
38573
|
if (!testFiles.includes(testFile)) {
|
|
38172
38574
|
testFiles.push(testFile);
|
|
@@ -38181,8 +38583,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38181
38583
|
while (match !== null) {
|
|
38182
38584
|
const importPath = match[1];
|
|
38183
38585
|
if (importPath.startsWith(".")) {
|
|
38184
|
-
let resolvedImport =
|
|
38185
|
-
const existingExt =
|
|
38586
|
+
let resolvedImport = path22.resolve(testDir, importPath);
|
|
38587
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38186
38588
|
if (!existingExt) {
|
|
38187
38589
|
for (const extToTry of [
|
|
38188
38590
|
".ts",
|
|
@@ -38193,18 +38595,18 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38193
38595
|
".cjs"
|
|
38194
38596
|
]) {
|
|
38195
38597
|
const withExt = resolvedImport + extToTry;
|
|
38196
|
-
if (sourceFiles.includes(withExt) ||
|
|
38598
|
+
if (sourceFiles.includes(withExt) || fs12.existsSync(withExt)) {
|
|
38197
38599
|
resolvedImport = withExt;
|
|
38198
38600
|
break;
|
|
38199
38601
|
}
|
|
38200
38602
|
}
|
|
38201
38603
|
}
|
|
38202
|
-
const importDir =
|
|
38203
|
-
const importBasename =
|
|
38604
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38605
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38204
38606
|
for (const sourceFile of sourceFiles) {
|
|
38205
|
-
const sourceDir =
|
|
38206
|
-
const sourceBasename =
|
|
38207
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38607
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38608
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38609
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38208
38610
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38209
38611
|
if (!testFiles.includes(testFile)) {
|
|
38210
38612
|
testFiles.push(testFile);
|
|
@@ -38289,8 +38691,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38289
38691
|
return ["mvn", "test"];
|
|
38290
38692
|
case "gradle": {
|
|
38291
38693
|
const isWindows = process.platform === "win32";
|
|
38292
|
-
const hasGradlewBat =
|
|
38293
|
-
const hasGradlew =
|
|
38694
|
+
const hasGradlewBat = fs12.existsSync(path22.join(baseDir, "gradlew.bat"));
|
|
38695
|
+
const hasGradlew = fs12.existsSync(path22.join(baseDir, "gradlew"));
|
|
38294
38696
|
if (hasGradlewBat && isWindows)
|
|
38295
38697
|
return ["gradlew.bat", "test"];
|
|
38296
38698
|
if (hasGradlew)
|
|
@@ -38307,7 +38709,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38307
38709
|
"cmake-build-release",
|
|
38308
38710
|
"out"
|
|
38309
38711
|
];
|
|
38310
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
38712
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path22.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
38311
38713
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
38312
38714
|
}
|
|
38313
38715
|
case "swift-test":
|
|
@@ -38849,7 +39251,7 @@ var test_runner = createSwarmTool({
|
|
|
38849
39251
|
let effectiveScope = scope;
|
|
38850
39252
|
if (scope === "all") {} else if (scope === "convention") {
|
|
38851
39253
|
const sourceFiles = args.files.filter((f) => {
|
|
38852
|
-
const ext =
|
|
39254
|
+
const ext = path22.extname(f).toLowerCase();
|
|
38853
39255
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38854
39256
|
});
|
|
38855
39257
|
if (sourceFiles.length === 0) {
|
|
@@ -38865,7 +39267,7 @@ var test_runner = createSwarmTool({
|
|
|
38865
39267
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
38866
39268
|
} else if (scope === "graph") {
|
|
38867
39269
|
const sourceFiles = args.files.filter((f) => {
|
|
38868
|
-
const ext =
|
|
39270
|
+
const ext = path22.extname(f).toLowerCase();
|
|
38869
39271
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38870
39272
|
});
|
|
38871
39273
|
if (sourceFiles.length === 0) {
|
|
@@ -38936,8 +39338,8 @@ function validateDirectoryPath(dir) {
|
|
|
38936
39338
|
if (dir.includes("..")) {
|
|
38937
39339
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
38938
39340
|
}
|
|
38939
|
-
const normalized =
|
|
38940
|
-
const absolutePath =
|
|
39341
|
+
const normalized = path23.normalize(dir);
|
|
39342
|
+
const absolutePath = path23.isAbsolute(normalized) ? normalized : path23.resolve(normalized);
|
|
38941
39343
|
return absolutePath;
|
|
38942
39344
|
}
|
|
38943
39345
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -38960,9 +39362,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
38960
39362
|
}
|
|
38961
39363
|
function getPackageVersion(dir) {
|
|
38962
39364
|
try {
|
|
38963
|
-
const packagePath =
|
|
38964
|
-
if (
|
|
38965
|
-
const content =
|
|
39365
|
+
const packagePath = path23.join(dir, "package.json");
|
|
39366
|
+
if (fs13.existsSync(packagePath)) {
|
|
39367
|
+
const content = fs13.readFileSync(packagePath, "utf-8");
|
|
38966
39368
|
const pkg = JSON.parse(content);
|
|
38967
39369
|
return pkg.version ?? null;
|
|
38968
39370
|
}
|
|
@@ -38971,9 +39373,9 @@ function getPackageVersion(dir) {
|
|
|
38971
39373
|
}
|
|
38972
39374
|
function getChangelogVersion(dir) {
|
|
38973
39375
|
try {
|
|
38974
|
-
const changelogPath =
|
|
38975
|
-
if (
|
|
38976
|
-
const content =
|
|
39376
|
+
const changelogPath = path23.join(dir, "CHANGELOG.md");
|
|
39377
|
+
if (fs13.existsSync(changelogPath)) {
|
|
39378
|
+
const content = fs13.readFileSync(changelogPath, "utf-8");
|
|
38977
39379
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
38978
39380
|
if (match) {
|
|
38979
39381
|
return match[1];
|
|
@@ -38985,10 +39387,10 @@ function getChangelogVersion(dir) {
|
|
|
38985
39387
|
function getVersionFileVersion(dir) {
|
|
38986
39388
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
38987
39389
|
for (const file3 of possibleFiles) {
|
|
38988
|
-
const filePath =
|
|
38989
|
-
if (
|
|
39390
|
+
const filePath = path23.join(dir, file3);
|
|
39391
|
+
if (fs13.existsSync(filePath)) {
|
|
38990
39392
|
try {
|
|
38991
|
-
const content =
|
|
39393
|
+
const content = fs13.readFileSync(filePath, "utf-8").trim();
|
|
38992
39394
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
38993
39395
|
if (match) {
|
|
38994
39396
|
return match[1];
|
|
@@ -39532,7 +39934,7 @@ async function handlePromoteCommand(directory, args) {
|
|
|
39532
39934
|
}
|
|
39533
39935
|
|
|
39534
39936
|
// src/commands/reset.ts
|
|
39535
|
-
import * as
|
|
39937
|
+
import * as fs14 from "fs";
|
|
39536
39938
|
|
|
39537
39939
|
// src/background/manager.ts
|
|
39538
39940
|
init_utils();
|
|
@@ -40233,8 +40635,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40233
40635
|
for (const filename of filesToReset) {
|
|
40234
40636
|
try {
|
|
40235
40637
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
40236
|
-
if (
|
|
40237
|
-
|
|
40638
|
+
if (fs14.existsSync(resolvedPath)) {
|
|
40639
|
+
fs14.unlinkSync(resolvedPath);
|
|
40238
40640
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
40239
40641
|
} else {
|
|
40240
40642
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -40251,8 +40653,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40251
40653
|
}
|
|
40252
40654
|
try {
|
|
40253
40655
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
40254
|
-
if (
|
|
40255
|
-
|
|
40656
|
+
if (fs14.existsSync(summariesPath)) {
|
|
40657
|
+
fs14.rmSync(summariesPath, { recursive: true, force: true });
|
|
40256
40658
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
40257
40659
|
} else {
|
|
40258
40660
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -40272,14 +40674,14 @@ async function handleResetCommand(directory, args) {
|
|
|
40272
40674
|
|
|
40273
40675
|
// src/commands/reset-session.ts
|
|
40274
40676
|
init_utils2();
|
|
40275
|
-
import * as
|
|
40276
|
-
import * as
|
|
40677
|
+
import * as fs15 from "fs";
|
|
40678
|
+
import * as path24 from "path";
|
|
40277
40679
|
async function handleResetSessionCommand(directory, _args) {
|
|
40278
40680
|
const results = [];
|
|
40279
40681
|
try {
|
|
40280
40682
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
40281
|
-
if (
|
|
40282
|
-
|
|
40683
|
+
if (fs15.existsSync(statePath)) {
|
|
40684
|
+
fs15.unlinkSync(statePath);
|
|
40283
40685
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
40284
40686
|
} else {
|
|
40285
40687
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -40288,15 +40690,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40288
40690
|
results.push("\u274C Failed to delete state.json");
|
|
40289
40691
|
}
|
|
40290
40692
|
try {
|
|
40291
|
-
const sessionDir =
|
|
40292
|
-
if (
|
|
40293
|
-
const files =
|
|
40693
|
+
const sessionDir = path24.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
40694
|
+
if (fs15.existsSync(sessionDir)) {
|
|
40695
|
+
const files = fs15.readdirSync(sessionDir);
|
|
40294
40696
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
40295
40697
|
let deletedCount = 0;
|
|
40296
40698
|
for (const file3 of otherFiles) {
|
|
40297
|
-
const filePath =
|
|
40298
|
-
if (
|
|
40299
|
-
|
|
40699
|
+
const filePath = path24.join(sessionDir, file3);
|
|
40700
|
+
if (fs15.lstatSync(filePath).isFile()) {
|
|
40701
|
+
fs15.unlinkSync(filePath);
|
|
40300
40702
|
deletedCount++;
|
|
40301
40703
|
}
|
|
40302
40704
|
}
|
|
@@ -40324,7 +40726,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40324
40726
|
// src/summaries/manager.ts
|
|
40325
40727
|
init_utils2();
|
|
40326
40728
|
init_utils();
|
|
40327
|
-
import * as
|
|
40729
|
+
import * as path25 from "path";
|
|
40328
40730
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
40329
40731
|
function sanitizeSummaryId(id) {
|
|
40330
40732
|
if (!id || id.length === 0) {
|
|
@@ -40348,7 +40750,7 @@ function sanitizeSummaryId(id) {
|
|
|
40348
40750
|
}
|
|
40349
40751
|
async function loadFullOutput(directory, id) {
|
|
40350
40752
|
const sanitizedId = sanitizeSummaryId(id);
|
|
40351
|
-
const relativePath =
|
|
40753
|
+
const relativePath = path25.join("summaries", `${sanitizedId}.json`);
|
|
40352
40754
|
validateSwarmPath(directory, relativePath);
|
|
40353
40755
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
40354
40756
|
if (content === null) {
|
|
@@ -40401,18 +40803,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
40401
40803
|
|
|
40402
40804
|
// src/commands/rollback.ts
|
|
40403
40805
|
init_utils2();
|
|
40404
|
-
import * as
|
|
40405
|
-
import * as
|
|
40806
|
+
import * as fs16 from "fs";
|
|
40807
|
+
import * as path26 from "path";
|
|
40406
40808
|
async function handleRollbackCommand(directory, args) {
|
|
40407
40809
|
const phaseArg = args[0];
|
|
40408
40810
|
if (!phaseArg) {
|
|
40409
40811
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40410
|
-
if (!
|
|
40812
|
+
if (!fs16.existsSync(manifestPath2)) {
|
|
40411
40813
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
40412
40814
|
}
|
|
40413
40815
|
let manifest2;
|
|
40414
40816
|
try {
|
|
40415
|
-
manifest2 = JSON.parse(
|
|
40817
|
+
manifest2 = JSON.parse(fs16.readFileSync(manifestPath2, "utf-8"));
|
|
40416
40818
|
} catch {
|
|
40417
40819
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
40418
40820
|
}
|
|
@@ -40434,12 +40836,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40434
40836
|
return "Error: Phase number must be a positive integer.";
|
|
40435
40837
|
}
|
|
40436
40838
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40437
|
-
if (!
|
|
40839
|
+
if (!fs16.existsSync(manifestPath)) {
|
|
40438
40840
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
40439
40841
|
}
|
|
40440
40842
|
let manifest;
|
|
40441
40843
|
try {
|
|
40442
|
-
manifest = JSON.parse(
|
|
40844
|
+
manifest = JSON.parse(fs16.readFileSync(manifestPath, "utf-8"));
|
|
40443
40845
|
} catch {
|
|
40444
40846
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
40445
40847
|
}
|
|
@@ -40449,10 +40851,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40449
40851
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
40450
40852
|
}
|
|
40451
40853
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
40452
|
-
if (!
|
|
40854
|
+
if (!fs16.existsSync(checkpointDir)) {
|
|
40453
40855
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
40454
40856
|
}
|
|
40455
|
-
const checkpointFiles =
|
|
40857
|
+
const checkpointFiles = fs16.readdirSync(checkpointDir);
|
|
40456
40858
|
if (checkpointFiles.length === 0) {
|
|
40457
40859
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
40458
40860
|
}
|
|
@@ -40460,10 +40862,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40460
40862
|
const successes = [];
|
|
40461
40863
|
const failures = [];
|
|
40462
40864
|
for (const file3 of checkpointFiles) {
|
|
40463
|
-
const src =
|
|
40464
|
-
const dest =
|
|
40865
|
+
const src = path26.join(checkpointDir, file3);
|
|
40866
|
+
const dest = path26.join(swarmDir, file3);
|
|
40465
40867
|
try {
|
|
40466
|
-
|
|
40868
|
+
fs16.cpSync(src, dest, { recursive: true, force: true });
|
|
40467
40869
|
successes.push(file3);
|
|
40468
40870
|
} catch (error93) {
|
|
40469
40871
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -40480,7 +40882,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40480
40882
|
timestamp: new Date().toISOString()
|
|
40481
40883
|
};
|
|
40482
40884
|
try {
|
|
40483
|
-
|
|
40885
|
+
fs16.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
40484
40886
|
`);
|
|
40485
40887
|
} catch (error93) {
|
|
40486
40888
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -40523,11 +40925,11 @@ async function handleSimulateCommand(directory, args) {
|
|
|
40523
40925
|
];
|
|
40524
40926
|
const report = reportLines.filter(Boolean).join(`
|
|
40525
40927
|
`);
|
|
40526
|
-
const
|
|
40527
|
-
const
|
|
40528
|
-
const reportPath =
|
|
40529
|
-
await
|
|
40530
|
-
await
|
|
40928
|
+
const fs17 = await import("fs/promises");
|
|
40929
|
+
const path27 = await import("path");
|
|
40930
|
+
const reportPath = path27.join(directory, ".swarm", "simulate-report.md");
|
|
40931
|
+
await fs17.mkdir(path27.dirname(reportPath), { recursive: true });
|
|
40932
|
+
await fs17.writeFile(reportPath, report, "utf-8");
|
|
40531
40933
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
40532
40934
|
}
|
|
40533
40935
|
|
|
@@ -41009,18 +41411,18 @@ function resolveCommand(tokens) {
|
|
|
41009
41411
|
}
|
|
41010
41412
|
|
|
41011
41413
|
// src/cli/index.ts
|
|
41012
|
-
var CONFIG_DIR =
|
|
41013
|
-
var OPENCODE_CONFIG_PATH =
|
|
41014
|
-
var PLUGIN_CONFIG_PATH =
|
|
41015
|
-
var PROMPTS_DIR =
|
|
41414
|
+
var CONFIG_DIR = path27.join(process.env.XDG_CONFIG_HOME || path27.join(os5.homedir(), ".config"), "opencode");
|
|
41415
|
+
var OPENCODE_CONFIG_PATH = path27.join(CONFIG_DIR, "opencode.json");
|
|
41416
|
+
var PLUGIN_CONFIG_PATH = path27.join(CONFIG_DIR, "opencode-swarm.json");
|
|
41417
|
+
var PROMPTS_DIR = path27.join(CONFIG_DIR, "opencode-swarm");
|
|
41016
41418
|
function ensureDir(dir) {
|
|
41017
|
-
if (!
|
|
41018
|
-
|
|
41419
|
+
if (!fs17.existsSync(dir)) {
|
|
41420
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
41019
41421
|
}
|
|
41020
41422
|
}
|
|
41021
41423
|
function loadJson(filepath) {
|
|
41022
41424
|
try {
|
|
41023
|
-
const content =
|
|
41425
|
+
const content = fs17.readFileSync(filepath, "utf-8");
|
|
41024
41426
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
41025
41427
|
return JSON.parse(stripped);
|
|
41026
41428
|
} catch {
|
|
@@ -41028,7 +41430,7 @@ function loadJson(filepath) {
|
|
|
41028
41430
|
}
|
|
41029
41431
|
}
|
|
41030
41432
|
function saveJson(filepath, data) {
|
|
41031
|
-
|
|
41433
|
+
fs17.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
41032
41434
|
`, "utf-8");
|
|
41033
41435
|
}
|
|
41034
41436
|
async function install() {
|
|
@@ -41036,7 +41438,7 @@ async function install() {
|
|
|
41036
41438
|
`);
|
|
41037
41439
|
ensureDir(CONFIG_DIR);
|
|
41038
41440
|
ensureDir(PROMPTS_DIR);
|
|
41039
|
-
const LEGACY_CONFIG_PATH =
|
|
41441
|
+
const LEGACY_CONFIG_PATH = path27.join(CONFIG_DIR, "config.json");
|
|
41040
41442
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41041
41443
|
if (!opencodeConfig) {
|
|
41042
41444
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -41061,7 +41463,7 @@ async function install() {
|
|
|
41061
41463
|
saveJson(OPENCODE_CONFIG_PATH, opencodeConfig);
|
|
41062
41464
|
console.log("\u2713 Added opencode-swarm to OpenCode plugins");
|
|
41063
41465
|
console.log("\u2713 Disabled default OpenCode agents (explore, general)");
|
|
41064
|
-
if (!
|
|
41466
|
+
if (!fs17.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41065
41467
|
const defaultConfig = {
|
|
41066
41468
|
agents: {
|
|
41067
41469
|
coder: { model: "opencode/minimax-m2.5-free" },
|
|
@@ -41104,7 +41506,7 @@ async function uninstall() {
|
|
|
41104
41506
|
`);
|
|
41105
41507
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41106
41508
|
if (!opencodeConfig) {
|
|
41107
|
-
if (
|
|
41509
|
+
if (fs17.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
41108
41510
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
41109
41511
|
return 1;
|
|
41110
41512
|
} else {
|
|
@@ -41136,13 +41538,13 @@ async function uninstall() {
|
|
|
41136
41538
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
41137
41539
|
if (process.argv.includes("--clean")) {
|
|
41138
41540
|
let cleaned = false;
|
|
41139
|
-
if (
|
|
41140
|
-
|
|
41541
|
+
if (fs17.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41542
|
+
fs17.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
41141
41543
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
41142
41544
|
cleaned = true;
|
|
41143
41545
|
}
|
|
41144
|
-
if (
|
|
41145
|
-
|
|
41546
|
+
if (fs17.existsSync(PROMPTS_DIR)) {
|
|
41547
|
+
fs17.rmSync(PROMPTS_DIR, { recursive: true });
|
|
41146
41548
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
41147
41549
|
cleaned = true;
|
|
41148
41550
|
}
|