opencode-swarm 6.43.2 → 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 +745 -345
- package/dist/config/constants.d.ts +13 -0
- package/dist/index.js +1627 -1113
- 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) {
|
|
@@ -31620,7 +31998,7 @@ async function handleClarifyCommand(_directory, args) {
|
|
|
31620
31998
|
}
|
|
31621
31999
|
|
|
31622
32000
|
// src/commands/close.ts
|
|
31623
|
-
import { promises as
|
|
32001
|
+
import { promises as fs6 } from "fs";
|
|
31624
32002
|
init_manager();
|
|
31625
32003
|
|
|
31626
32004
|
// src/hooks/knowledge-store.ts
|
|
@@ -32256,10 +32634,31 @@ async function runAutoPromotion(directory, config3) {
|
|
|
32256
32634
|
// src/commands/close.ts
|
|
32257
32635
|
init_utils2();
|
|
32258
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
|
+
|
|
32259
32658
|
// src/session/snapshot-writer.ts
|
|
32260
32659
|
init_utils2();
|
|
32261
|
-
import { mkdirSync as
|
|
32262
|
-
import * as
|
|
32660
|
+
import { mkdirSync as mkdirSync4, renameSync as renameSync4 } from "fs";
|
|
32661
|
+
import * as path10 from "path";
|
|
32263
32662
|
init_utils();
|
|
32264
32663
|
var _writeInFlight = Promise.resolve();
|
|
32265
32664
|
function serializeAgentSession(s) {
|
|
@@ -32346,11 +32745,11 @@ async function writeSnapshot(directory, state) {
|
|
|
32346
32745
|
}
|
|
32347
32746
|
const content = JSON.stringify(snapshot, null, 2);
|
|
32348
32747
|
const resolvedPath = validateSwarmPath(directory, "session/state.json");
|
|
32349
|
-
const dir =
|
|
32350
|
-
|
|
32748
|
+
const dir = path10.dirname(resolvedPath);
|
|
32749
|
+
mkdirSync4(dir, { recursive: true });
|
|
32351
32750
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
32352
32751
|
await Bun.write(tempPath, content);
|
|
32353
|
-
|
|
32752
|
+
renameSync4(tempPath, resolvedPath);
|
|
32354
32753
|
} catch (error93) {
|
|
32355
32754
|
log("[snapshot-writer] write failed", {
|
|
32356
32755
|
error: error93 instanceof Error ? error93.message : String(error93)
|
|
@@ -32709,7 +33108,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32709
33108
|
const planPath = validateSwarmPath(directory, "plan.json");
|
|
32710
33109
|
let planData;
|
|
32711
33110
|
try {
|
|
32712
|
-
const content = await
|
|
33111
|
+
const content = await fs6.readFile(planPath, "utf-8");
|
|
32713
33112
|
planData = JSON.parse(content);
|
|
32714
33113
|
} catch (error93) {
|
|
32715
33114
|
return `\u274C Failed to read plan.json: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
@@ -32776,7 +33175,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32776
33175
|
}
|
|
32777
33176
|
}
|
|
32778
33177
|
try {
|
|
32779
|
-
await
|
|
33178
|
+
await fs6.writeFile(planPath, JSON.stringify(planData, null, 2), "utf-8");
|
|
32780
33179
|
} catch (error93) {
|
|
32781
33180
|
console.warn("[close-command] Failed to write plan.json:", error93);
|
|
32782
33181
|
}
|
|
@@ -32808,7 +33207,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32808
33207
|
].join(`
|
|
32809
33208
|
`);
|
|
32810
33209
|
try {
|
|
32811
|
-
await
|
|
33210
|
+
await fs6.writeFile(closeSummaryPath, summaryContent, "utf-8");
|
|
32812
33211
|
} catch (error93) {
|
|
32813
33212
|
console.warn("[close-command] Failed to write close-summary.md:", error93);
|
|
32814
33213
|
}
|
|
@@ -32817,6 +33216,7 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32817
33216
|
} catch (error93) {
|
|
32818
33217
|
console.warn("[close-command] flushPendingSnapshot error:", error93);
|
|
32819
33218
|
}
|
|
33219
|
+
await writeCheckpoint(directory).catch(() => {});
|
|
32820
33220
|
swarmState.agentSessions.clear();
|
|
32821
33221
|
swarmState.delegationChains.clear();
|
|
32822
33222
|
const warningMsg = warnings.length > 0 ? ` Warnings: ${warnings.join("; ")}.` : "";
|
|
@@ -32825,14 +33225,14 @@ async function handleCloseCommand(directory, _args) {
|
|
|
32825
33225
|
|
|
32826
33226
|
// src/commands/config.ts
|
|
32827
33227
|
import * as os3 from "os";
|
|
32828
|
-
import * as
|
|
33228
|
+
import * as path11 from "path";
|
|
32829
33229
|
function getUserConfigDir2() {
|
|
32830
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
33230
|
+
return process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config");
|
|
32831
33231
|
}
|
|
32832
33232
|
async function handleConfigCommand(directory, _args) {
|
|
32833
33233
|
const config3 = loadPluginConfig(directory);
|
|
32834
|
-
const userConfigPath =
|
|
32835
|
-
const projectConfigPath =
|
|
33234
|
+
const userConfigPath = path11.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
|
|
33235
|
+
const projectConfigPath = path11.join(directory, ".opencode", "opencode-swarm.json");
|
|
32836
33236
|
const lines = [
|
|
32837
33237
|
"## Swarm Configuration",
|
|
32838
33238
|
"",
|
|
@@ -32850,7 +33250,7 @@ async function handleConfigCommand(directory, _args) {
|
|
|
32850
33250
|
}
|
|
32851
33251
|
|
|
32852
33252
|
// src/hooks/hive-promoter.ts
|
|
32853
|
-
import
|
|
33253
|
+
import path12 from "path";
|
|
32854
33254
|
|
|
32855
33255
|
// src/background/event-bus.ts
|
|
32856
33256
|
init_utils();
|
|
@@ -33105,7 +33505,7 @@ async function promoteToHive(directory, lesson, category) {
|
|
|
33105
33505
|
schema_version: 1,
|
|
33106
33506
|
created_at: new Date().toISOString(),
|
|
33107
33507
|
updated_at: new Date().toISOString(),
|
|
33108
|
-
source_project:
|
|
33508
|
+
source_project: path12.basename(directory) || "unknown",
|
|
33109
33509
|
encounter_score: 1
|
|
33110
33510
|
};
|
|
33111
33511
|
await appendKnowledge(resolveHiveKnowledgePath(), newHiveEntry);
|
|
@@ -33183,13 +33583,13 @@ function formatCurationSummary(summary) {
|
|
|
33183
33583
|
}
|
|
33184
33584
|
|
|
33185
33585
|
// src/commands/dark-matter.ts
|
|
33186
|
-
import
|
|
33586
|
+
import path14 from "path";
|
|
33187
33587
|
|
|
33188
33588
|
// src/tools/co-change-analyzer.ts
|
|
33189
33589
|
import * as child_process from "child_process";
|
|
33190
33590
|
import { randomUUID } from "crypto";
|
|
33191
33591
|
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
33192
|
-
import * as
|
|
33592
|
+
import * as path13 from "path";
|
|
33193
33593
|
import { promisify } from "util";
|
|
33194
33594
|
function getExecFileAsync() {
|
|
33195
33595
|
return promisify(child_process.execFile);
|
|
@@ -33291,7 +33691,7 @@ async function scanSourceFiles(dir) {
|
|
|
33291
33691
|
try {
|
|
33292
33692
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
33293
33693
|
for (const entry of entries) {
|
|
33294
|
-
const fullPath =
|
|
33694
|
+
const fullPath = path13.join(dir, entry.name);
|
|
33295
33695
|
if (entry.isDirectory()) {
|
|
33296
33696
|
if (skipDirs.has(entry.name)) {
|
|
33297
33697
|
continue;
|
|
@@ -33299,7 +33699,7 @@ async function scanSourceFiles(dir) {
|
|
|
33299
33699
|
const subFiles = await scanSourceFiles(fullPath);
|
|
33300
33700
|
results.push(...subFiles);
|
|
33301
33701
|
} else if (entry.isFile()) {
|
|
33302
|
-
const ext =
|
|
33702
|
+
const ext = path13.extname(entry.name);
|
|
33303
33703
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
|
|
33304
33704
|
results.push(fullPath);
|
|
33305
33705
|
}
|
|
@@ -33321,8 +33721,8 @@ async function getStaticEdges(directory) {
|
|
|
33321
33721
|
continue;
|
|
33322
33722
|
}
|
|
33323
33723
|
try {
|
|
33324
|
-
const sourceDir =
|
|
33325
|
-
const resolvedPath =
|
|
33724
|
+
const sourceDir = path13.dirname(sourceFile);
|
|
33725
|
+
const resolvedPath = path13.resolve(sourceDir, importPath);
|
|
33326
33726
|
const extensions = [
|
|
33327
33727
|
"",
|
|
33328
33728
|
".ts",
|
|
@@ -33347,8 +33747,8 @@ async function getStaticEdges(directory) {
|
|
|
33347
33747
|
if (!targetFile) {
|
|
33348
33748
|
continue;
|
|
33349
33749
|
}
|
|
33350
|
-
const relSource =
|
|
33351
|
-
const relTarget =
|
|
33750
|
+
const relSource = path13.relative(directory, sourceFile).replace(/\\/g, "/");
|
|
33751
|
+
const relTarget = path13.relative(directory, targetFile).replace(/\\/g, "/");
|
|
33352
33752
|
const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
|
|
33353
33753
|
edges.add(key);
|
|
33354
33754
|
} catch {}
|
|
@@ -33360,7 +33760,7 @@ async function getStaticEdges(directory) {
|
|
|
33360
33760
|
function isTestImplementationPair(fileA, fileB) {
|
|
33361
33761
|
const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
|
|
33362
33762
|
const getBaseName = (filePath) => {
|
|
33363
|
-
const base =
|
|
33763
|
+
const base = path13.basename(filePath);
|
|
33364
33764
|
for (const pattern of testPatterns) {
|
|
33365
33765
|
if (base.endsWith(pattern)) {
|
|
33366
33766
|
return base.slice(0, -pattern.length);
|
|
@@ -33370,16 +33770,16 @@ function isTestImplementationPair(fileA, fileB) {
|
|
|
33370
33770
|
};
|
|
33371
33771
|
const baseA = getBaseName(fileA);
|
|
33372
33772
|
const baseB = getBaseName(fileB);
|
|
33373
|
-
return baseA === baseB && baseA !==
|
|
33773
|
+
return baseA === baseB && baseA !== path13.basename(fileA) && baseA !== path13.basename(fileB);
|
|
33374
33774
|
}
|
|
33375
33775
|
function hasSharedPrefix(fileA, fileB) {
|
|
33376
|
-
const dirA =
|
|
33377
|
-
const dirB =
|
|
33776
|
+
const dirA = path13.dirname(fileA);
|
|
33777
|
+
const dirB = path13.dirname(fileB);
|
|
33378
33778
|
if (dirA !== dirB) {
|
|
33379
33779
|
return false;
|
|
33380
33780
|
}
|
|
33381
|
-
const baseA =
|
|
33382
|
-
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)$/, "");
|
|
33383
33783
|
if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
|
|
33384
33784
|
return true;
|
|
33385
33785
|
}
|
|
@@ -33433,8 +33833,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
33433
33833
|
const entries = [];
|
|
33434
33834
|
const now = new Date().toISOString();
|
|
33435
33835
|
for (const pair of pairs.slice(0, 10)) {
|
|
33436
|
-
const baseA =
|
|
33437
|
-
const baseB =
|
|
33836
|
+
const baseA = path13.basename(pair.fileA);
|
|
33837
|
+
const baseB = path13.basename(pair.fileB);
|
|
33438
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.`;
|
|
33439
33839
|
if (lesson.length > 280) {
|
|
33440
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.`;
|
|
@@ -33544,7 +33944,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33544
33944
|
const output = formatDarkMatterOutput(pairs);
|
|
33545
33945
|
if (pairs.length > 0) {
|
|
33546
33946
|
try {
|
|
33547
|
-
const projectName =
|
|
33947
|
+
const projectName = path14.basename(path14.resolve(directory));
|
|
33548
33948
|
const entries = darkMatterToKnowledgeEntries(pairs, projectName);
|
|
33549
33949
|
if (entries.length > 0) {
|
|
33550
33950
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -33565,8 +33965,8 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
33565
33965
|
|
|
33566
33966
|
// src/services/diagnose-service.ts
|
|
33567
33967
|
import { execSync } from "child_process";
|
|
33568
|
-
import { existsSync as
|
|
33569
|
-
import
|
|
33968
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
33969
|
+
import path15 from "path";
|
|
33570
33970
|
import { fileURLToPath } from "url";
|
|
33571
33971
|
init_manager();
|
|
33572
33972
|
init_utils2();
|
|
@@ -33802,7 +34202,7 @@ async function checkConfigBackups(directory) {
|
|
|
33802
34202
|
}
|
|
33803
34203
|
async function checkGitRepository(directory) {
|
|
33804
34204
|
try {
|
|
33805
|
-
if (!
|
|
34205
|
+
if (!existsSync5(directory) || !statSync3(directory).isDirectory()) {
|
|
33806
34206
|
return {
|
|
33807
34207
|
name: "Git Repository",
|
|
33808
34208
|
status: "\u274C",
|
|
@@ -33863,8 +34263,8 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
33863
34263
|
};
|
|
33864
34264
|
}
|
|
33865
34265
|
async function checkConfigParseability(directory) {
|
|
33866
|
-
const configPath =
|
|
33867
|
-
if (!
|
|
34266
|
+
const configPath = path15.join(directory, ".opencode/opencode-swarm.json");
|
|
34267
|
+
if (!existsSync5(configPath)) {
|
|
33868
34268
|
return {
|
|
33869
34269
|
name: "Config Parseability",
|
|
33870
34270
|
status: "\u2705",
|
|
@@ -33872,7 +34272,7 @@ async function checkConfigParseability(directory) {
|
|
|
33872
34272
|
};
|
|
33873
34273
|
}
|
|
33874
34274
|
try {
|
|
33875
|
-
const content =
|
|
34275
|
+
const content = readFileSync5(configPath, "utf-8");
|
|
33876
34276
|
JSON.parse(content);
|
|
33877
34277
|
return {
|
|
33878
34278
|
name: "Config Parseability",
|
|
@@ -33910,15 +34310,15 @@ async function checkGrammarWasmFiles() {
|
|
|
33910
34310
|
"tree-sitter-ini.wasm",
|
|
33911
34311
|
"tree-sitter-regex.wasm"
|
|
33912
34312
|
];
|
|
33913
|
-
const thisDir =
|
|
34313
|
+
const thisDir = path15.dirname(fileURLToPath(import.meta.url));
|
|
33914
34314
|
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
33915
|
-
const grammarDir = isSource ?
|
|
34315
|
+
const grammarDir = isSource ? path15.join(thisDir, "..", "lang", "grammars") : path15.join(thisDir, "lang", "grammars");
|
|
33916
34316
|
const missing = [];
|
|
33917
|
-
if (!
|
|
34317
|
+
if (!existsSync5(path15.join(grammarDir, "tree-sitter.wasm"))) {
|
|
33918
34318
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
33919
34319
|
}
|
|
33920
34320
|
for (const file3 of grammarFiles) {
|
|
33921
|
-
if (!
|
|
34321
|
+
if (!existsSync5(path15.join(grammarDir, file3))) {
|
|
33922
34322
|
missing.push(file3);
|
|
33923
34323
|
}
|
|
33924
34324
|
}
|
|
@@ -33936,8 +34336,8 @@ async function checkGrammarWasmFiles() {
|
|
|
33936
34336
|
};
|
|
33937
34337
|
}
|
|
33938
34338
|
async function checkCheckpointManifest(directory) {
|
|
33939
|
-
const manifestPath =
|
|
33940
|
-
if (!
|
|
34339
|
+
const manifestPath = path15.join(directory, ".swarm/checkpoints.json");
|
|
34340
|
+
if (!existsSync5(manifestPath)) {
|
|
33941
34341
|
return {
|
|
33942
34342
|
name: "Checkpoint Manifest",
|
|
33943
34343
|
status: "\u2705",
|
|
@@ -33945,7 +34345,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
33945
34345
|
};
|
|
33946
34346
|
}
|
|
33947
34347
|
try {
|
|
33948
|
-
const content =
|
|
34348
|
+
const content = readFileSync5(manifestPath, "utf-8");
|
|
33949
34349
|
const parsed = JSON.parse(content);
|
|
33950
34350
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
33951
34351
|
return {
|
|
@@ -33988,8 +34388,8 @@ async function checkCheckpointManifest(directory) {
|
|
|
33988
34388
|
}
|
|
33989
34389
|
}
|
|
33990
34390
|
async function checkEventStreamIntegrity(directory) {
|
|
33991
|
-
const eventsPath =
|
|
33992
|
-
if (!
|
|
34391
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34392
|
+
if (!existsSync5(eventsPath)) {
|
|
33993
34393
|
return {
|
|
33994
34394
|
name: "Event Stream",
|
|
33995
34395
|
status: "\u2705",
|
|
@@ -33997,7 +34397,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
33997
34397
|
};
|
|
33998
34398
|
}
|
|
33999
34399
|
try {
|
|
34000
|
-
const content =
|
|
34400
|
+
const content = readFileSync5(eventsPath, "utf-8");
|
|
34001
34401
|
const lines = content.split(`
|
|
34002
34402
|
`).filter((line) => line.trim() !== "");
|
|
34003
34403
|
let malformedCount = 0;
|
|
@@ -34029,8 +34429,8 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
34029
34429
|
}
|
|
34030
34430
|
}
|
|
34031
34431
|
async function checkSteeringDirectives(directory) {
|
|
34032
|
-
const eventsPath =
|
|
34033
|
-
if (!
|
|
34432
|
+
const eventsPath = path15.join(directory, ".swarm/events.jsonl");
|
|
34433
|
+
if (!existsSync5(eventsPath)) {
|
|
34034
34434
|
return {
|
|
34035
34435
|
name: "Steering Directives",
|
|
34036
34436
|
status: "\u2705",
|
|
@@ -34038,7 +34438,7 @@ async function checkSteeringDirectives(directory) {
|
|
|
34038
34438
|
};
|
|
34039
34439
|
}
|
|
34040
34440
|
try {
|
|
34041
|
-
const content =
|
|
34441
|
+
const content = readFileSync5(eventsPath, "utf-8");
|
|
34042
34442
|
const lines = content.split(`
|
|
34043
34443
|
`).filter((line) => line.trim() !== "");
|
|
34044
34444
|
const directivesIssued = [];
|
|
@@ -34085,8 +34485,8 @@ async function checkCurator(directory) {
|
|
|
34085
34485
|
detail: "Disabled (enable via curator.enabled)"
|
|
34086
34486
|
};
|
|
34087
34487
|
}
|
|
34088
|
-
const summaryPath =
|
|
34089
|
-
if (!
|
|
34488
|
+
const summaryPath = path15.join(directory, ".swarm/curator-summary.json");
|
|
34489
|
+
if (!existsSync5(summaryPath)) {
|
|
34090
34490
|
return {
|
|
34091
34491
|
name: "Curator",
|
|
34092
34492
|
status: "\u2705",
|
|
@@ -34094,7 +34494,7 @@ async function checkCurator(directory) {
|
|
|
34094
34494
|
};
|
|
34095
34495
|
}
|
|
34096
34496
|
try {
|
|
34097
|
-
const content =
|
|
34497
|
+
const content = readFileSync5(summaryPath, "utf-8");
|
|
34098
34498
|
const parsed = JSON.parse(content);
|
|
34099
34499
|
if (typeof parsed.schema_version !== "number" || parsed.schema_version !== 1) {
|
|
34100
34500
|
return {
|
|
@@ -34524,8 +34924,8 @@ async function handleExportCommand(directory, _args) {
|
|
|
34524
34924
|
}
|
|
34525
34925
|
// src/commands/handoff.ts
|
|
34526
34926
|
init_utils2();
|
|
34527
|
-
import
|
|
34528
|
-
import { renameSync as
|
|
34927
|
+
import crypto4 from "crypto";
|
|
34928
|
+
import { renameSync as renameSync5 } from "fs";
|
|
34529
34929
|
|
|
34530
34930
|
// src/services/handoff-service.ts
|
|
34531
34931
|
init_utils2();
|
|
@@ -34833,9 +35233,9 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
34833
35233
|
const handoffData = await getHandoffData(directory);
|
|
34834
35234
|
const markdown = formatHandoffMarkdown(handoffData);
|
|
34835
35235
|
const resolvedPath = validateSwarmPath(directory, "handoff.md");
|
|
34836
|
-
const tempPath = `${resolvedPath}.tmp.${
|
|
35236
|
+
const tempPath = `${resolvedPath}.tmp.${crypto4.randomUUID()}`;
|
|
34837
35237
|
await Bun.write(tempPath, markdown);
|
|
34838
|
-
|
|
35238
|
+
renameSync5(tempPath, resolvedPath);
|
|
34839
35239
|
await writeSnapshot(directory, swarmState);
|
|
34840
35240
|
await flushPendingSnapshot(directory);
|
|
34841
35241
|
return `## Handoff Brief Written
|
|
@@ -34980,14 +35380,14 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
34980
35380
|
}
|
|
34981
35381
|
// src/hooks/knowledge-migrator.ts
|
|
34982
35382
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
34983
|
-
import { existsSync as
|
|
35383
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
34984
35384
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
34985
|
-
import * as
|
|
35385
|
+
import * as path17 from "path";
|
|
34986
35386
|
async function migrateContextToKnowledge(directory, config3) {
|
|
34987
|
-
const sentinelPath =
|
|
34988
|
-
const contextPath =
|
|
35387
|
+
const sentinelPath = path17.join(directory, ".swarm", ".knowledge-migrated");
|
|
35388
|
+
const contextPath = path17.join(directory, ".swarm", "context.md");
|
|
34989
35389
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
34990
|
-
if (
|
|
35390
|
+
if (existsSync7(sentinelPath)) {
|
|
34991
35391
|
return {
|
|
34992
35392
|
migrated: false,
|
|
34993
35393
|
entriesMigrated: 0,
|
|
@@ -34996,7 +35396,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
34996
35396
|
skippedReason: "sentinel-exists"
|
|
34997
35397
|
};
|
|
34998
35398
|
}
|
|
34999
|
-
if (!
|
|
35399
|
+
if (!existsSync7(contextPath)) {
|
|
35000
35400
|
return {
|
|
35001
35401
|
migrated: false,
|
|
35002
35402
|
entriesMigrated: 0,
|
|
@@ -35181,16 +35581,16 @@ function truncateLesson(text) {
|
|
|
35181
35581
|
return `${text.slice(0, 277)}...`;
|
|
35182
35582
|
}
|
|
35183
35583
|
function inferProjectName(directory) {
|
|
35184
|
-
const packageJsonPath =
|
|
35185
|
-
if (
|
|
35584
|
+
const packageJsonPath = path17.join(directory, "package.json");
|
|
35585
|
+
if (existsSync7(packageJsonPath)) {
|
|
35186
35586
|
try {
|
|
35187
|
-
const pkg = JSON.parse(
|
|
35587
|
+
const pkg = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
35188
35588
|
if (pkg.name && typeof pkg.name === "string") {
|
|
35189
35589
|
return pkg.name;
|
|
35190
35590
|
}
|
|
35191
35591
|
} catch {}
|
|
35192
35592
|
}
|
|
35193
|
-
return
|
|
35593
|
+
return path17.basename(directory);
|
|
35194
35594
|
}
|
|
35195
35595
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
35196
35596
|
const sentinel = {
|
|
@@ -35202,7 +35602,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
35202
35602
|
schema_version: 1,
|
|
35203
35603
|
migration_tool: "knowledge-migrator.ts"
|
|
35204
35604
|
};
|
|
35205
|
-
await mkdir3(
|
|
35605
|
+
await mkdir3(path17.dirname(sentinelPath), { recursive: true });
|
|
35206
35606
|
await writeFile3(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
35207
35607
|
}
|
|
35208
35608
|
|
|
@@ -35438,20 +35838,20 @@ async function handlePlanCommand(directory, args) {
|
|
|
35438
35838
|
// src/services/preflight-service.ts
|
|
35439
35839
|
init_manager();
|
|
35440
35840
|
init_manager2();
|
|
35441
|
-
import * as
|
|
35442
|
-
import * as
|
|
35841
|
+
import * as fs13 from "fs";
|
|
35842
|
+
import * as path23 from "path";
|
|
35443
35843
|
|
|
35444
35844
|
// src/tools/lint.ts
|
|
35445
|
-
import * as
|
|
35446
|
-
import * as
|
|
35845
|
+
import * as fs9 from "fs";
|
|
35846
|
+
import * as path19 from "path";
|
|
35447
35847
|
|
|
35448
35848
|
// src/build/discovery.ts
|
|
35449
|
-
import * as
|
|
35450
|
-
import * as
|
|
35849
|
+
import * as fs8 from "fs";
|
|
35850
|
+
import * as path18 from "path";
|
|
35451
35851
|
|
|
35452
35852
|
// src/lang/detector.ts
|
|
35453
35853
|
import { access, readdir as readdir2 } from "fs/promises";
|
|
35454
|
-
import { extname as extname2, join as
|
|
35854
|
+
import { extname as extname2, join as join13 } from "path";
|
|
35455
35855
|
|
|
35456
35856
|
// src/lang/profiles.ts
|
|
35457
35857
|
class LanguageRegistry {
|
|
@@ -36376,7 +36776,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
36376
36776
|
if (detectFile.includes("*") || detectFile.includes("?"))
|
|
36377
36777
|
continue;
|
|
36378
36778
|
try {
|
|
36379
|
-
await access(
|
|
36779
|
+
await access(join13(dir, detectFile));
|
|
36380
36780
|
detected.add(profile.id);
|
|
36381
36781
|
break;
|
|
36382
36782
|
} catch {}
|
|
@@ -36397,7 +36797,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
36397
36797
|
const topEntries = await readdir2(projectDir, { withFileTypes: true });
|
|
36398
36798
|
for (const entry of topEntries) {
|
|
36399
36799
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
36400
|
-
await scanDir(
|
|
36800
|
+
await scanDir(join13(projectDir, entry.name));
|
|
36401
36801
|
}
|
|
36402
36802
|
}
|
|
36403
36803
|
} catch {}
|
|
@@ -36540,16 +36940,16 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36540
36940
|
if (pattern.includes("*")) {
|
|
36541
36941
|
const dir = workingDir;
|
|
36542
36942
|
try {
|
|
36543
|
-
const files =
|
|
36943
|
+
const files = fs8.readdirSync(dir);
|
|
36544
36944
|
const regex = simpleGlobToRegex(pattern);
|
|
36545
36945
|
const matches = files.filter((f) => regex.test(f));
|
|
36546
36946
|
if (matches.length > 0) {
|
|
36547
|
-
return
|
|
36947
|
+
return path18.join(dir, matches[0]);
|
|
36548
36948
|
}
|
|
36549
36949
|
} catch {}
|
|
36550
36950
|
} else {
|
|
36551
|
-
const filePath =
|
|
36552
|
-
if (
|
|
36951
|
+
const filePath = path18.join(workingDir, pattern);
|
|
36952
|
+
if (fs8.existsSync(filePath)) {
|
|
36553
36953
|
return filePath;
|
|
36554
36954
|
}
|
|
36555
36955
|
}
|
|
@@ -36557,12 +36957,12 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
36557
36957
|
return null;
|
|
36558
36958
|
}
|
|
36559
36959
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
36560
|
-
const packageJsonPath =
|
|
36561
|
-
if (!
|
|
36960
|
+
const packageJsonPath = path18.join(workingDir, "package.json");
|
|
36961
|
+
if (!fs8.existsSync(packageJsonPath)) {
|
|
36562
36962
|
return [];
|
|
36563
36963
|
}
|
|
36564
36964
|
try {
|
|
36565
|
-
const content =
|
|
36965
|
+
const content = fs8.readFileSync(packageJsonPath, "utf-8");
|
|
36566
36966
|
const pkg = JSON.parse(content);
|
|
36567
36967
|
if (!pkg.scripts || typeof pkg.scripts !== "object") {
|
|
36568
36968
|
return [];
|
|
@@ -36598,8 +36998,8 @@ function findAllBuildFiles(workingDir) {
|
|
|
36598
36998
|
const regex = simpleGlobToRegex(pattern);
|
|
36599
36999
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
36600
37000
|
} else {
|
|
36601
|
-
const filePath =
|
|
36602
|
-
if (
|
|
37001
|
+
const filePath = path18.join(workingDir, pattern);
|
|
37002
|
+
if (fs8.existsSync(filePath)) {
|
|
36603
37003
|
allBuildFiles.add(filePath);
|
|
36604
37004
|
}
|
|
36605
37005
|
}
|
|
@@ -36609,9 +37009,9 @@ function findAllBuildFiles(workingDir) {
|
|
|
36609
37009
|
}
|
|
36610
37010
|
function findFilesRecursive(dir, regex, results) {
|
|
36611
37011
|
try {
|
|
36612
|
-
const entries =
|
|
37012
|
+
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
36613
37013
|
for (const entry of entries) {
|
|
36614
|
-
const fullPath =
|
|
37014
|
+
const fullPath = path18.join(dir, entry.name);
|
|
36615
37015
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
36616
37016
|
findFilesRecursive(fullPath, regex, results);
|
|
36617
37017
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -36634,8 +37034,8 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
|
|
|
36634
37034
|
let foundCommand = false;
|
|
36635
37035
|
for (const cmd of sortedCommands) {
|
|
36636
37036
|
if (cmd.detectFile) {
|
|
36637
|
-
const detectFilePath =
|
|
36638
|
-
if (!
|
|
37037
|
+
const detectFilePath = path18.join(workingDir, cmd.detectFile);
|
|
37038
|
+
if (!fs8.existsSync(detectFilePath)) {
|
|
36639
37039
|
continue;
|
|
36640
37040
|
}
|
|
36641
37041
|
}
|
|
@@ -36782,9 +37182,9 @@ function validateArgs(args) {
|
|
|
36782
37182
|
}
|
|
36783
37183
|
function getLinterCommand(linter, mode, projectDir) {
|
|
36784
37184
|
const isWindows = process.platform === "win32";
|
|
36785
|
-
const binDir =
|
|
36786
|
-
const biomeBin = isWindows ?
|
|
36787
|
-
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");
|
|
36788
37188
|
switch (linter) {
|
|
36789
37189
|
case "biome":
|
|
36790
37190
|
if (mode === "fix") {
|
|
@@ -36800,7 +37200,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
36800
37200
|
}
|
|
36801
37201
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
36802
37202
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
36803
|
-
const gradlew =
|
|
37203
|
+
const gradlew = fs9.existsSync(path19.join(cwd, gradlewName)) ? path19.join(cwd, gradlewName) : null;
|
|
36804
37204
|
switch (linter) {
|
|
36805
37205
|
case "ruff":
|
|
36806
37206
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -36834,12 +37234,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
36834
37234
|
}
|
|
36835
37235
|
}
|
|
36836
37236
|
function detectRuff(cwd) {
|
|
36837
|
-
if (
|
|
37237
|
+
if (fs9.existsSync(path19.join(cwd, "ruff.toml")))
|
|
36838
37238
|
return isCommandAvailable("ruff");
|
|
36839
37239
|
try {
|
|
36840
|
-
const pyproject =
|
|
36841
|
-
if (
|
|
36842
|
-
const content =
|
|
37240
|
+
const pyproject = path19.join(cwd, "pyproject.toml");
|
|
37241
|
+
if (fs9.existsSync(pyproject)) {
|
|
37242
|
+
const content = fs9.readFileSync(pyproject, "utf-8");
|
|
36843
37243
|
if (content.includes("[tool.ruff]"))
|
|
36844
37244
|
return isCommandAvailable("ruff");
|
|
36845
37245
|
}
|
|
@@ -36847,21 +37247,21 @@ function detectRuff(cwd) {
|
|
|
36847
37247
|
return false;
|
|
36848
37248
|
}
|
|
36849
37249
|
function detectClippy(cwd) {
|
|
36850
|
-
return
|
|
37250
|
+
return fs9.existsSync(path19.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
36851
37251
|
}
|
|
36852
37252
|
function detectGolangciLint(cwd) {
|
|
36853
|
-
return
|
|
37253
|
+
return fs9.existsSync(path19.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
36854
37254
|
}
|
|
36855
37255
|
function detectCheckstyle(cwd) {
|
|
36856
|
-
const hasMaven =
|
|
36857
|
-
const hasGradle =
|
|
36858
|
-
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"));
|
|
36859
37259
|
return (hasMaven || hasGradle) && hasBinary;
|
|
36860
37260
|
}
|
|
36861
37261
|
function detectKtlint(cwd) {
|
|
36862
|
-
const hasKotlin =
|
|
37262
|
+
const hasKotlin = fs9.existsSync(path19.join(cwd, "build.gradle.kts")) || fs9.existsSync(path19.join(cwd, "build.gradle")) || (() => {
|
|
36863
37263
|
try {
|
|
36864
|
-
return
|
|
37264
|
+
return fs9.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
36865
37265
|
} catch {
|
|
36866
37266
|
return false;
|
|
36867
37267
|
}
|
|
@@ -36870,7 +37270,7 @@ function detectKtlint(cwd) {
|
|
|
36870
37270
|
}
|
|
36871
37271
|
function detectDotnetFormat(cwd) {
|
|
36872
37272
|
try {
|
|
36873
|
-
const files =
|
|
37273
|
+
const files = fs9.readdirSync(cwd);
|
|
36874
37274
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
36875
37275
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
36876
37276
|
} catch {
|
|
@@ -36878,14 +37278,14 @@ function detectDotnetFormat(cwd) {
|
|
|
36878
37278
|
}
|
|
36879
37279
|
}
|
|
36880
37280
|
function detectCppcheck(cwd) {
|
|
36881
|
-
if (
|
|
37281
|
+
if (fs9.existsSync(path19.join(cwd, "CMakeLists.txt"))) {
|
|
36882
37282
|
return isCommandAvailable("cppcheck");
|
|
36883
37283
|
}
|
|
36884
37284
|
try {
|
|
36885
|
-
const dirsToCheck = [cwd,
|
|
37285
|
+
const dirsToCheck = [cwd, path19.join(cwd, "src")];
|
|
36886
37286
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
36887
37287
|
try {
|
|
36888
|
-
return
|
|
37288
|
+
return fs9.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
36889
37289
|
} catch {
|
|
36890
37290
|
return false;
|
|
36891
37291
|
}
|
|
@@ -36896,13 +37296,13 @@ function detectCppcheck(cwd) {
|
|
|
36896
37296
|
}
|
|
36897
37297
|
}
|
|
36898
37298
|
function detectSwiftlint(cwd) {
|
|
36899
|
-
return
|
|
37299
|
+
return fs9.existsSync(path19.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
36900
37300
|
}
|
|
36901
37301
|
function detectDartAnalyze(cwd) {
|
|
36902
|
-
return
|
|
37302
|
+
return fs9.existsSync(path19.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
36903
37303
|
}
|
|
36904
37304
|
function detectRubocop(cwd) {
|
|
36905
|
-
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"));
|
|
36906
37306
|
}
|
|
36907
37307
|
function detectAdditionalLinter(cwd) {
|
|
36908
37308
|
if (detectRuff(cwd))
|
|
@@ -36931,12 +37331,12 @@ async function detectAvailableLinter(directory) {
|
|
|
36931
37331
|
const _DETECT_TIMEOUT = 2000;
|
|
36932
37332
|
if (!directory)
|
|
36933
37333
|
return null;
|
|
36934
|
-
if (!
|
|
37334
|
+
if (!fs9.existsSync(directory))
|
|
36935
37335
|
return null;
|
|
36936
37336
|
const projectDir = directory;
|
|
36937
37337
|
const isWindows = process.platform === "win32";
|
|
36938
|
-
const biomeBin = isWindows ?
|
|
36939
|
-
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");
|
|
36940
37340
|
return _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
36941
37341
|
}
|
|
36942
37342
|
async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
@@ -36951,7 +37351,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36951
37351
|
const result = await Promise.race([biomeExit, timeout]);
|
|
36952
37352
|
if (result === "timeout") {
|
|
36953
37353
|
biomeProc.kill();
|
|
36954
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
37354
|
+
} else if (biomeProc.exitCode === 0 && fs9.existsSync(biomeBin)) {
|
|
36955
37355
|
return "biome";
|
|
36956
37356
|
}
|
|
36957
37357
|
} catch {}
|
|
@@ -36965,7 +37365,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36965
37365
|
const result = await Promise.race([eslintExit, timeout]);
|
|
36966
37366
|
if (result === "timeout") {
|
|
36967
37367
|
eslintProc.kill();
|
|
36968
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
37368
|
+
} else if (eslintProc.exitCode === 0 && fs9.existsSync(eslintBin)) {
|
|
36969
37369
|
return "eslint";
|
|
36970
37370
|
}
|
|
36971
37371
|
} catch {}
|
|
@@ -37135,8 +37535,8 @@ For Rust: rustup component add clippy`
|
|
|
37135
37535
|
});
|
|
37136
37536
|
|
|
37137
37537
|
// src/tools/secretscan.ts
|
|
37138
|
-
import * as
|
|
37139
|
-
import * as
|
|
37538
|
+
import * as fs10 from "fs";
|
|
37539
|
+
import * as path20 from "path";
|
|
37140
37540
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
37141
37541
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
37142
37542
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -37363,11 +37763,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
37363
37763
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
37364
37764
|
}
|
|
37365
37765
|
function loadSecretScanIgnore(scanDir) {
|
|
37366
|
-
const ignorePath =
|
|
37766
|
+
const ignorePath = path20.join(scanDir, ".secretscanignore");
|
|
37367
37767
|
try {
|
|
37368
|
-
if (!
|
|
37768
|
+
if (!fs10.existsSync(ignorePath))
|
|
37369
37769
|
return [];
|
|
37370
|
-
const content =
|
|
37770
|
+
const content = fs10.readFileSync(ignorePath, "utf8");
|
|
37371
37771
|
const patterns = [];
|
|
37372
37772
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
37373
37773
|
const line = rawLine.trim();
|
|
@@ -37386,7 +37786,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
37386
37786
|
if (exactNames.has(entry))
|
|
37387
37787
|
return true;
|
|
37388
37788
|
for (const pattern of globPatterns) {
|
|
37389
|
-
if (
|
|
37789
|
+
if (path20.matchesGlob(relPath, pattern))
|
|
37390
37790
|
return true;
|
|
37391
37791
|
}
|
|
37392
37792
|
return false;
|
|
@@ -37407,7 +37807,7 @@ function validateDirectoryInput(dir) {
|
|
|
37407
37807
|
return null;
|
|
37408
37808
|
}
|
|
37409
37809
|
function isBinaryFile(filePath, buffer) {
|
|
37410
|
-
const ext =
|
|
37810
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
37411
37811
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37412
37812
|
return true;
|
|
37413
37813
|
}
|
|
@@ -37482,11 +37882,11 @@ function createRedactedContext(line, findings) {
|
|
|
37482
37882
|
result += line.slice(lastEnd);
|
|
37483
37883
|
return result;
|
|
37484
37884
|
}
|
|
37485
|
-
var O_NOFOLLOW = process.platform !== "win32" ?
|
|
37885
|
+
var O_NOFOLLOW = process.platform !== "win32" ? fs10.constants.O_NOFOLLOW : undefined;
|
|
37486
37886
|
function scanFileForSecrets(filePath) {
|
|
37487
37887
|
const findings = [];
|
|
37488
37888
|
try {
|
|
37489
|
-
const lstat =
|
|
37889
|
+
const lstat = fs10.lstatSync(filePath);
|
|
37490
37890
|
if (lstat.isSymbolicLink()) {
|
|
37491
37891
|
return findings;
|
|
37492
37892
|
}
|
|
@@ -37495,14 +37895,14 @@ function scanFileForSecrets(filePath) {
|
|
|
37495
37895
|
}
|
|
37496
37896
|
let buffer;
|
|
37497
37897
|
if (O_NOFOLLOW !== undefined) {
|
|
37498
|
-
const fd =
|
|
37898
|
+
const fd = fs10.openSync(filePath, "r", O_NOFOLLOW);
|
|
37499
37899
|
try {
|
|
37500
|
-
buffer =
|
|
37900
|
+
buffer = fs10.readFileSync(fd);
|
|
37501
37901
|
} finally {
|
|
37502
|
-
|
|
37902
|
+
fs10.closeSync(fd);
|
|
37503
37903
|
}
|
|
37504
37904
|
} else {
|
|
37505
|
-
buffer =
|
|
37905
|
+
buffer = fs10.readFileSync(filePath);
|
|
37506
37906
|
}
|
|
37507
37907
|
if (isBinaryFile(filePath, buffer)) {
|
|
37508
37908
|
return findings;
|
|
@@ -37544,9 +37944,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
37544
37944
|
return false;
|
|
37545
37945
|
}
|
|
37546
37946
|
function isPathWithinScope(realPath, scanDir) {
|
|
37547
|
-
const resolvedScanDir =
|
|
37548
|
-
const resolvedRealPath =
|
|
37549
|
-
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}\\`);
|
|
37550
37950
|
}
|
|
37551
37951
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
37552
37952
|
skippedDirs: 0,
|
|
@@ -37557,7 +37957,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37557
37957
|
const files = [];
|
|
37558
37958
|
let entries;
|
|
37559
37959
|
try {
|
|
37560
|
-
entries =
|
|
37960
|
+
entries = fs10.readdirSync(dir);
|
|
37561
37961
|
} catch {
|
|
37562
37962
|
stats.fileErrors++;
|
|
37563
37963
|
return files;
|
|
@@ -37572,15 +37972,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37572
37972
|
return a.localeCompare(b);
|
|
37573
37973
|
});
|
|
37574
37974
|
for (const entry of entries) {
|
|
37575
|
-
const fullPath =
|
|
37576
|
-
const relPath =
|
|
37975
|
+
const fullPath = path20.join(dir, entry);
|
|
37976
|
+
const relPath = path20.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
37577
37977
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
37578
37978
|
stats.skippedDirs++;
|
|
37579
37979
|
continue;
|
|
37580
37980
|
}
|
|
37581
37981
|
let lstat;
|
|
37582
37982
|
try {
|
|
37583
|
-
lstat =
|
|
37983
|
+
lstat = fs10.lstatSync(fullPath);
|
|
37584
37984
|
} catch {
|
|
37585
37985
|
stats.fileErrors++;
|
|
37586
37986
|
continue;
|
|
@@ -37592,7 +37992,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37592
37992
|
if (lstat.isDirectory()) {
|
|
37593
37993
|
let realPath;
|
|
37594
37994
|
try {
|
|
37595
|
-
realPath =
|
|
37995
|
+
realPath = fs10.realpathSync(fullPath);
|
|
37596
37996
|
} catch {
|
|
37597
37997
|
stats.fileErrors++;
|
|
37598
37998
|
continue;
|
|
@@ -37608,7 +38008,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37608
38008
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
37609
38009
|
files.push(...subFiles);
|
|
37610
38010
|
} else if (lstat.isFile()) {
|
|
37611
|
-
const ext =
|
|
38011
|
+
const ext = path20.extname(fullPath).toLowerCase();
|
|
37612
38012
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37613
38013
|
files.push(fullPath);
|
|
37614
38014
|
} else {
|
|
@@ -37674,8 +38074,8 @@ var secretscan = createSwarmTool({
|
|
|
37674
38074
|
}
|
|
37675
38075
|
}
|
|
37676
38076
|
try {
|
|
37677
|
-
const scanDir =
|
|
37678
|
-
if (!
|
|
38077
|
+
const scanDir = path20.resolve(directory);
|
|
38078
|
+
if (!fs10.existsSync(scanDir)) {
|
|
37679
38079
|
const errorResult = {
|
|
37680
38080
|
error: "directory not found",
|
|
37681
38081
|
scan_dir: directory,
|
|
@@ -37686,7 +38086,7 @@ var secretscan = createSwarmTool({
|
|
|
37686
38086
|
};
|
|
37687
38087
|
return JSON.stringify(errorResult, null, 2);
|
|
37688
38088
|
}
|
|
37689
|
-
const dirStat =
|
|
38089
|
+
const dirStat = fs10.statSync(scanDir);
|
|
37690
38090
|
if (!dirStat.isDirectory()) {
|
|
37691
38091
|
const errorResult = {
|
|
37692
38092
|
error: "target must be a directory, not a file",
|
|
@@ -37737,7 +38137,7 @@ var secretscan = createSwarmTool({
|
|
|
37737
38137
|
break;
|
|
37738
38138
|
const fileFindings = scanFileForSecrets(filePath);
|
|
37739
38139
|
try {
|
|
37740
|
-
const stat2 =
|
|
38140
|
+
const stat2 = fs10.statSync(filePath);
|
|
37741
38141
|
if (stat2.size > MAX_FILE_SIZE_BYTES) {
|
|
37742
38142
|
skippedFiles++;
|
|
37743
38143
|
continue;
|
|
@@ -37824,12 +38224,12 @@ async function runSecretscan(directory) {
|
|
|
37824
38224
|
}
|
|
37825
38225
|
|
|
37826
38226
|
// src/tools/test-runner.ts
|
|
37827
|
-
import * as
|
|
37828
|
-
import * as
|
|
38227
|
+
import * as fs12 from "fs";
|
|
38228
|
+
import * as path22 from "path";
|
|
37829
38229
|
|
|
37830
38230
|
// src/tools/resolve-working-directory.ts
|
|
37831
|
-
import * as
|
|
37832
|
-
import * as
|
|
38231
|
+
import * as fs11 from "fs";
|
|
38232
|
+
import * as path21 from "path";
|
|
37833
38233
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
37834
38234
|
if (workingDirectory == null || workingDirectory === "") {
|
|
37835
38235
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -37849,17 +38249,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
37849
38249
|
};
|
|
37850
38250
|
}
|
|
37851
38251
|
}
|
|
37852
|
-
const normalizedDir =
|
|
37853
|
-
const pathParts = normalizedDir.split(
|
|
38252
|
+
const normalizedDir = path21.normalize(workingDirectory);
|
|
38253
|
+
const pathParts = normalizedDir.split(path21.sep);
|
|
37854
38254
|
if (pathParts.includes("..")) {
|
|
37855
38255
|
return {
|
|
37856
38256
|
success: false,
|
|
37857
38257
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
37858
38258
|
};
|
|
37859
38259
|
}
|
|
37860
|
-
const resolvedDir =
|
|
38260
|
+
const resolvedDir = path21.resolve(normalizedDir);
|
|
37861
38261
|
try {
|
|
37862
|
-
const realPath =
|
|
38262
|
+
const realPath = fs11.realpathSync(resolvedDir);
|
|
37863
38263
|
return { success: true, directory: realPath };
|
|
37864
38264
|
} catch {
|
|
37865
38265
|
return {
|
|
@@ -37940,19 +38340,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
37940
38340
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
37941
38341
|
}
|
|
37942
38342
|
function detectGoTest(cwd) {
|
|
37943
|
-
return
|
|
38343
|
+
return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
37944
38344
|
}
|
|
37945
38345
|
function detectJavaMaven(cwd) {
|
|
37946
|
-
return
|
|
38346
|
+
return fs12.existsSync(path22.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
37947
38347
|
}
|
|
37948
38348
|
function detectGradle(cwd) {
|
|
37949
|
-
const hasBuildFile =
|
|
37950
|
-
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"));
|
|
37951
38351
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
37952
38352
|
}
|
|
37953
38353
|
function detectDotnetTest(cwd) {
|
|
37954
38354
|
try {
|
|
37955
|
-
const files =
|
|
38355
|
+
const files = fs12.readdirSync(cwd);
|
|
37956
38356
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
37957
38357
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
37958
38358
|
} catch {
|
|
@@ -37960,32 +38360,32 @@ function detectDotnetTest(cwd) {
|
|
|
37960
38360
|
}
|
|
37961
38361
|
}
|
|
37962
38362
|
function detectCTest(cwd) {
|
|
37963
|
-
const hasSource =
|
|
37964
|
-
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"));
|
|
37965
38365
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
37966
38366
|
}
|
|
37967
38367
|
function detectSwiftTest(cwd) {
|
|
37968
|
-
return
|
|
38368
|
+
return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
37969
38369
|
}
|
|
37970
38370
|
function detectDartTest(cwd) {
|
|
37971
|
-
return
|
|
38371
|
+
return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
37972
38372
|
}
|
|
37973
38373
|
function detectRSpec(cwd) {
|
|
37974
|
-
const hasRSpecFile =
|
|
37975
|
-
const hasGemfile =
|
|
37976
|
-
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"));
|
|
37977
38377
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
37978
38378
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
37979
38379
|
}
|
|
37980
38380
|
function detectMinitest(cwd) {
|
|
37981
|
-
return
|
|
38381
|
+
return fs12.existsSync(path22.join(cwd, "test")) && (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
37982
38382
|
}
|
|
37983
38383
|
async function detectTestFramework(cwd) {
|
|
37984
38384
|
const baseDir = cwd;
|
|
37985
38385
|
try {
|
|
37986
|
-
const packageJsonPath =
|
|
37987
|
-
if (
|
|
37988
|
-
const content =
|
|
38386
|
+
const packageJsonPath = path22.join(baseDir, "package.json");
|
|
38387
|
+
if (fs12.existsSync(packageJsonPath)) {
|
|
38388
|
+
const content = fs12.readFileSync(packageJsonPath, "utf-8");
|
|
37989
38389
|
const pkg = JSON.parse(content);
|
|
37990
38390
|
const _deps = pkg.dependencies || {};
|
|
37991
38391
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -38004,38 +38404,38 @@ async function detectTestFramework(cwd) {
|
|
|
38004
38404
|
return "jest";
|
|
38005
38405
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
38006
38406
|
return "mocha";
|
|
38007
|
-
if (
|
|
38407
|
+
if (fs12.existsSync(path22.join(baseDir, "bun.lockb")) || fs12.existsSync(path22.join(baseDir, "bun.lock"))) {
|
|
38008
38408
|
if (scripts.test?.includes("bun"))
|
|
38009
38409
|
return "bun";
|
|
38010
38410
|
}
|
|
38011
38411
|
}
|
|
38012
38412
|
} catch {}
|
|
38013
38413
|
try {
|
|
38014
|
-
const pyprojectTomlPath =
|
|
38015
|
-
const setupCfgPath =
|
|
38016
|
-
const requirementsTxtPath =
|
|
38017
|
-
if (
|
|
38018
|
-
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");
|
|
38019
38419
|
if (content.includes("[tool.pytest"))
|
|
38020
38420
|
return "pytest";
|
|
38021
38421
|
if (content.includes("pytest"))
|
|
38022
38422
|
return "pytest";
|
|
38023
38423
|
}
|
|
38024
|
-
if (
|
|
38025
|
-
const content =
|
|
38424
|
+
if (fs12.existsSync(setupCfgPath)) {
|
|
38425
|
+
const content = fs12.readFileSync(setupCfgPath, "utf-8");
|
|
38026
38426
|
if (content.includes("[pytest]"))
|
|
38027
38427
|
return "pytest";
|
|
38028
38428
|
}
|
|
38029
|
-
if (
|
|
38030
|
-
const content =
|
|
38429
|
+
if (fs12.existsSync(requirementsTxtPath)) {
|
|
38430
|
+
const content = fs12.readFileSync(requirementsTxtPath, "utf-8");
|
|
38031
38431
|
if (content.includes("pytest"))
|
|
38032
38432
|
return "pytest";
|
|
38033
38433
|
}
|
|
38034
38434
|
} catch {}
|
|
38035
38435
|
try {
|
|
38036
|
-
const cargoTomlPath =
|
|
38037
|
-
if (
|
|
38038
|
-
const content =
|
|
38436
|
+
const cargoTomlPath = path22.join(baseDir, "Cargo.toml");
|
|
38437
|
+
if (fs12.existsSync(cargoTomlPath)) {
|
|
38438
|
+
const content = fs12.readFileSync(cargoTomlPath, "utf-8");
|
|
38039
38439
|
if (content.includes("[dev-dependencies]")) {
|
|
38040
38440
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
38041
38441
|
return "cargo";
|
|
@@ -38044,10 +38444,10 @@ async function detectTestFramework(cwd) {
|
|
|
38044
38444
|
}
|
|
38045
38445
|
} catch {}
|
|
38046
38446
|
try {
|
|
38047
|
-
const pesterConfigPath =
|
|
38048
|
-
const pesterConfigJsonPath =
|
|
38049
|
-
const pesterPs1Path =
|
|
38050
|
-
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)) {
|
|
38051
38451
|
return "pester";
|
|
38052
38452
|
}
|
|
38053
38453
|
} catch {}
|
|
@@ -38098,8 +38498,8 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38098
38498
|
const testFiles = [];
|
|
38099
38499
|
for (const file3 of sourceFiles) {
|
|
38100
38500
|
const normalizedPath = file3.replace(/\\/g, "/");
|
|
38101
|
-
const basename4 =
|
|
38102
|
-
const dirname9 =
|
|
38501
|
+
const basename4 = path22.basename(file3);
|
|
38502
|
+
const dirname9 = path22.dirname(file3);
|
|
38103
38503
|
if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
38104
38504
|
if (!testFiles.includes(file3)) {
|
|
38105
38505
|
testFiles.push(file3);
|
|
@@ -38108,16 +38508,16 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38108
38508
|
}
|
|
38109
38509
|
for (const _pattern of TEST_PATTERNS) {
|
|
38110
38510
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
38111
|
-
const ext =
|
|
38511
|
+
const ext = path22.extname(basename4);
|
|
38112
38512
|
const possibleTestFiles = [
|
|
38113
|
-
|
|
38114
|
-
|
|
38115
|
-
|
|
38116
|
-
|
|
38117
|
-
|
|
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}`)
|
|
38118
38518
|
];
|
|
38119
38519
|
for (const testFile of possibleTestFiles) {
|
|
38120
|
-
if (
|
|
38520
|
+
if (fs12.existsSync(testFile) && !testFiles.includes(testFile)) {
|
|
38121
38521
|
testFiles.push(testFile);
|
|
38122
38522
|
}
|
|
38123
38523
|
}
|
|
@@ -38133,8 +38533,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38133
38533
|
}
|
|
38134
38534
|
for (const testFile of candidateTestFiles) {
|
|
38135
38535
|
try {
|
|
38136
|
-
const content =
|
|
38137
|
-
const testDir =
|
|
38536
|
+
const content = fs12.readFileSync(testFile, "utf-8");
|
|
38537
|
+
const testDir = path22.dirname(testFile);
|
|
38138
38538
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
38139
38539
|
let match;
|
|
38140
38540
|
match = importRegex.exec(content);
|
|
@@ -38142,8 +38542,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38142
38542
|
const importPath = match[1];
|
|
38143
38543
|
let resolvedImport;
|
|
38144
38544
|
if (importPath.startsWith(".")) {
|
|
38145
|
-
resolvedImport =
|
|
38146
|
-
const existingExt =
|
|
38545
|
+
resolvedImport = path22.resolve(testDir, importPath);
|
|
38546
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38147
38547
|
if (!existingExt) {
|
|
38148
38548
|
for (const extToTry of [
|
|
38149
38549
|
".ts",
|
|
@@ -38154,7 +38554,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38154
38554
|
".cjs"
|
|
38155
38555
|
]) {
|
|
38156
38556
|
const withExt = resolvedImport + extToTry;
|
|
38157
|
-
if (sourceFiles.includes(withExt) ||
|
|
38557
|
+
if (sourceFiles.includes(withExt) || fs12.existsSync(withExt)) {
|
|
38158
38558
|
resolvedImport = withExt;
|
|
38159
38559
|
break;
|
|
38160
38560
|
}
|
|
@@ -38163,12 +38563,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38163
38563
|
} else {
|
|
38164
38564
|
continue;
|
|
38165
38565
|
}
|
|
38166
|
-
const importBasename =
|
|
38167
|
-
const importDir =
|
|
38566
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38567
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38168
38568
|
for (const sourceFile of sourceFiles) {
|
|
38169
|
-
const sourceDir =
|
|
38170
|
-
const sourceBasename =
|
|
38171
|
-
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");
|
|
38172
38572
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38173
38573
|
if (!testFiles.includes(testFile)) {
|
|
38174
38574
|
testFiles.push(testFile);
|
|
@@ -38183,8 +38583,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38183
38583
|
while (match !== null) {
|
|
38184
38584
|
const importPath = match[1];
|
|
38185
38585
|
if (importPath.startsWith(".")) {
|
|
38186
|
-
let resolvedImport =
|
|
38187
|
-
const existingExt =
|
|
38586
|
+
let resolvedImport = path22.resolve(testDir, importPath);
|
|
38587
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38188
38588
|
if (!existingExt) {
|
|
38189
38589
|
for (const extToTry of [
|
|
38190
38590
|
".ts",
|
|
@@ -38195,18 +38595,18 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38195
38595
|
".cjs"
|
|
38196
38596
|
]) {
|
|
38197
38597
|
const withExt = resolvedImport + extToTry;
|
|
38198
|
-
if (sourceFiles.includes(withExt) ||
|
|
38598
|
+
if (sourceFiles.includes(withExt) || fs12.existsSync(withExt)) {
|
|
38199
38599
|
resolvedImport = withExt;
|
|
38200
38600
|
break;
|
|
38201
38601
|
}
|
|
38202
38602
|
}
|
|
38203
38603
|
}
|
|
38204
|
-
const importDir =
|
|
38205
|
-
const importBasename =
|
|
38604
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38605
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38206
38606
|
for (const sourceFile of sourceFiles) {
|
|
38207
|
-
const sourceDir =
|
|
38208
|
-
const sourceBasename =
|
|
38209
|
-
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");
|
|
38210
38610
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38211
38611
|
if (!testFiles.includes(testFile)) {
|
|
38212
38612
|
testFiles.push(testFile);
|
|
@@ -38291,8 +38691,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38291
38691
|
return ["mvn", "test"];
|
|
38292
38692
|
case "gradle": {
|
|
38293
38693
|
const isWindows = process.platform === "win32";
|
|
38294
|
-
const hasGradlewBat =
|
|
38295
|
-
const hasGradlew =
|
|
38694
|
+
const hasGradlewBat = fs12.existsSync(path22.join(baseDir, "gradlew.bat"));
|
|
38695
|
+
const hasGradlew = fs12.existsSync(path22.join(baseDir, "gradlew"));
|
|
38296
38696
|
if (hasGradlewBat && isWindows)
|
|
38297
38697
|
return ["gradlew.bat", "test"];
|
|
38298
38698
|
if (hasGradlew)
|
|
@@ -38309,7 +38709,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38309
38709
|
"cmake-build-release",
|
|
38310
38710
|
"out"
|
|
38311
38711
|
];
|
|
38312
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
38712
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path22.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
38313
38713
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
38314
38714
|
}
|
|
38315
38715
|
case "swift-test":
|
|
@@ -38851,7 +39251,7 @@ var test_runner = createSwarmTool({
|
|
|
38851
39251
|
let effectiveScope = scope;
|
|
38852
39252
|
if (scope === "all") {} else if (scope === "convention") {
|
|
38853
39253
|
const sourceFiles = args.files.filter((f) => {
|
|
38854
|
-
const ext =
|
|
39254
|
+
const ext = path22.extname(f).toLowerCase();
|
|
38855
39255
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38856
39256
|
});
|
|
38857
39257
|
if (sourceFiles.length === 0) {
|
|
@@ -38867,7 +39267,7 @@ var test_runner = createSwarmTool({
|
|
|
38867
39267
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
38868
39268
|
} else if (scope === "graph") {
|
|
38869
39269
|
const sourceFiles = args.files.filter((f) => {
|
|
38870
|
-
const ext =
|
|
39270
|
+
const ext = path22.extname(f).toLowerCase();
|
|
38871
39271
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38872
39272
|
});
|
|
38873
39273
|
if (sourceFiles.length === 0) {
|
|
@@ -38938,8 +39338,8 @@ function validateDirectoryPath(dir) {
|
|
|
38938
39338
|
if (dir.includes("..")) {
|
|
38939
39339
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
38940
39340
|
}
|
|
38941
|
-
const normalized =
|
|
38942
|
-
const absolutePath =
|
|
39341
|
+
const normalized = path23.normalize(dir);
|
|
39342
|
+
const absolutePath = path23.isAbsolute(normalized) ? normalized : path23.resolve(normalized);
|
|
38943
39343
|
return absolutePath;
|
|
38944
39344
|
}
|
|
38945
39345
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -38962,9 +39362,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
38962
39362
|
}
|
|
38963
39363
|
function getPackageVersion(dir) {
|
|
38964
39364
|
try {
|
|
38965
|
-
const packagePath =
|
|
38966
|
-
if (
|
|
38967
|
-
const content =
|
|
39365
|
+
const packagePath = path23.join(dir, "package.json");
|
|
39366
|
+
if (fs13.existsSync(packagePath)) {
|
|
39367
|
+
const content = fs13.readFileSync(packagePath, "utf-8");
|
|
38968
39368
|
const pkg = JSON.parse(content);
|
|
38969
39369
|
return pkg.version ?? null;
|
|
38970
39370
|
}
|
|
@@ -38973,9 +39373,9 @@ function getPackageVersion(dir) {
|
|
|
38973
39373
|
}
|
|
38974
39374
|
function getChangelogVersion(dir) {
|
|
38975
39375
|
try {
|
|
38976
|
-
const changelogPath =
|
|
38977
|
-
if (
|
|
38978
|
-
const content =
|
|
39376
|
+
const changelogPath = path23.join(dir, "CHANGELOG.md");
|
|
39377
|
+
if (fs13.existsSync(changelogPath)) {
|
|
39378
|
+
const content = fs13.readFileSync(changelogPath, "utf-8");
|
|
38979
39379
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
38980
39380
|
if (match) {
|
|
38981
39381
|
return match[1];
|
|
@@ -38987,10 +39387,10 @@ function getChangelogVersion(dir) {
|
|
|
38987
39387
|
function getVersionFileVersion(dir) {
|
|
38988
39388
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
38989
39389
|
for (const file3 of possibleFiles) {
|
|
38990
|
-
const filePath =
|
|
38991
|
-
if (
|
|
39390
|
+
const filePath = path23.join(dir, file3);
|
|
39391
|
+
if (fs13.existsSync(filePath)) {
|
|
38992
39392
|
try {
|
|
38993
|
-
const content =
|
|
39393
|
+
const content = fs13.readFileSync(filePath, "utf-8").trim();
|
|
38994
39394
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
38995
39395
|
if (match) {
|
|
38996
39396
|
return match[1];
|
|
@@ -39534,7 +39934,7 @@ async function handlePromoteCommand(directory, args) {
|
|
|
39534
39934
|
}
|
|
39535
39935
|
|
|
39536
39936
|
// src/commands/reset.ts
|
|
39537
|
-
import * as
|
|
39937
|
+
import * as fs14 from "fs";
|
|
39538
39938
|
|
|
39539
39939
|
// src/background/manager.ts
|
|
39540
39940
|
init_utils();
|
|
@@ -40235,8 +40635,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40235
40635
|
for (const filename of filesToReset) {
|
|
40236
40636
|
try {
|
|
40237
40637
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
40238
|
-
if (
|
|
40239
|
-
|
|
40638
|
+
if (fs14.existsSync(resolvedPath)) {
|
|
40639
|
+
fs14.unlinkSync(resolvedPath);
|
|
40240
40640
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
40241
40641
|
} else {
|
|
40242
40642
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -40253,8 +40653,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40253
40653
|
}
|
|
40254
40654
|
try {
|
|
40255
40655
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
40256
|
-
if (
|
|
40257
|
-
|
|
40656
|
+
if (fs14.existsSync(summariesPath)) {
|
|
40657
|
+
fs14.rmSync(summariesPath, { recursive: true, force: true });
|
|
40258
40658
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
40259
40659
|
} else {
|
|
40260
40660
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -40274,14 +40674,14 @@ async function handleResetCommand(directory, args) {
|
|
|
40274
40674
|
|
|
40275
40675
|
// src/commands/reset-session.ts
|
|
40276
40676
|
init_utils2();
|
|
40277
|
-
import * as
|
|
40278
|
-
import * as
|
|
40677
|
+
import * as fs15 from "fs";
|
|
40678
|
+
import * as path24 from "path";
|
|
40279
40679
|
async function handleResetSessionCommand(directory, _args) {
|
|
40280
40680
|
const results = [];
|
|
40281
40681
|
try {
|
|
40282
40682
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
40283
|
-
if (
|
|
40284
|
-
|
|
40683
|
+
if (fs15.existsSync(statePath)) {
|
|
40684
|
+
fs15.unlinkSync(statePath);
|
|
40285
40685
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
40286
40686
|
} else {
|
|
40287
40687
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -40290,15 +40690,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40290
40690
|
results.push("\u274C Failed to delete state.json");
|
|
40291
40691
|
}
|
|
40292
40692
|
try {
|
|
40293
|
-
const sessionDir =
|
|
40294
|
-
if (
|
|
40295
|
-
const files =
|
|
40693
|
+
const sessionDir = path24.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
40694
|
+
if (fs15.existsSync(sessionDir)) {
|
|
40695
|
+
const files = fs15.readdirSync(sessionDir);
|
|
40296
40696
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
40297
40697
|
let deletedCount = 0;
|
|
40298
40698
|
for (const file3 of otherFiles) {
|
|
40299
|
-
const filePath =
|
|
40300
|
-
if (
|
|
40301
|
-
|
|
40699
|
+
const filePath = path24.join(sessionDir, file3);
|
|
40700
|
+
if (fs15.lstatSync(filePath).isFile()) {
|
|
40701
|
+
fs15.unlinkSync(filePath);
|
|
40302
40702
|
deletedCount++;
|
|
40303
40703
|
}
|
|
40304
40704
|
}
|
|
@@ -40326,7 +40726,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40326
40726
|
// src/summaries/manager.ts
|
|
40327
40727
|
init_utils2();
|
|
40328
40728
|
init_utils();
|
|
40329
|
-
import * as
|
|
40729
|
+
import * as path25 from "path";
|
|
40330
40730
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
40331
40731
|
function sanitizeSummaryId(id) {
|
|
40332
40732
|
if (!id || id.length === 0) {
|
|
@@ -40350,7 +40750,7 @@ function sanitizeSummaryId(id) {
|
|
|
40350
40750
|
}
|
|
40351
40751
|
async function loadFullOutput(directory, id) {
|
|
40352
40752
|
const sanitizedId = sanitizeSummaryId(id);
|
|
40353
|
-
const relativePath =
|
|
40753
|
+
const relativePath = path25.join("summaries", `${sanitizedId}.json`);
|
|
40354
40754
|
validateSwarmPath(directory, relativePath);
|
|
40355
40755
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
40356
40756
|
if (content === null) {
|
|
@@ -40403,18 +40803,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
40403
40803
|
|
|
40404
40804
|
// src/commands/rollback.ts
|
|
40405
40805
|
init_utils2();
|
|
40406
|
-
import * as
|
|
40407
|
-
import * as
|
|
40806
|
+
import * as fs16 from "fs";
|
|
40807
|
+
import * as path26 from "path";
|
|
40408
40808
|
async function handleRollbackCommand(directory, args) {
|
|
40409
40809
|
const phaseArg = args[0];
|
|
40410
40810
|
if (!phaseArg) {
|
|
40411
40811
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40412
|
-
if (!
|
|
40812
|
+
if (!fs16.existsSync(manifestPath2)) {
|
|
40413
40813
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
40414
40814
|
}
|
|
40415
40815
|
let manifest2;
|
|
40416
40816
|
try {
|
|
40417
|
-
manifest2 = JSON.parse(
|
|
40817
|
+
manifest2 = JSON.parse(fs16.readFileSync(manifestPath2, "utf-8"));
|
|
40418
40818
|
} catch {
|
|
40419
40819
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
40420
40820
|
}
|
|
@@ -40436,12 +40836,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40436
40836
|
return "Error: Phase number must be a positive integer.";
|
|
40437
40837
|
}
|
|
40438
40838
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40439
|
-
if (!
|
|
40839
|
+
if (!fs16.existsSync(manifestPath)) {
|
|
40440
40840
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
40441
40841
|
}
|
|
40442
40842
|
let manifest;
|
|
40443
40843
|
try {
|
|
40444
|
-
manifest = JSON.parse(
|
|
40844
|
+
manifest = JSON.parse(fs16.readFileSync(manifestPath, "utf-8"));
|
|
40445
40845
|
} catch {
|
|
40446
40846
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
40447
40847
|
}
|
|
@@ -40451,10 +40851,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40451
40851
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
40452
40852
|
}
|
|
40453
40853
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
40454
|
-
if (!
|
|
40854
|
+
if (!fs16.existsSync(checkpointDir)) {
|
|
40455
40855
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
40456
40856
|
}
|
|
40457
|
-
const checkpointFiles =
|
|
40857
|
+
const checkpointFiles = fs16.readdirSync(checkpointDir);
|
|
40458
40858
|
if (checkpointFiles.length === 0) {
|
|
40459
40859
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
40460
40860
|
}
|
|
@@ -40462,10 +40862,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40462
40862
|
const successes = [];
|
|
40463
40863
|
const failures = [];
|
|
40464
40864
|
for (const file3 of checkpointFiles) {
|
|
40465
|
-
const src =
|
|
40466
|
-
const dest =
|
|
40865
|
+
const src = path26.join(checkpointDir, file3);
|
|
40866
|
+
const dest = path26.join(swarmDir, file3);
|
|
40467
40867
|
try {
|
|
40468
|
-
|
|
40868
|
+
fs16.cpSync(src, dest, { recursive: true, force: true });
|
|
40469
40869
|
successes.push(file3);
|
|
40470
40870
|
} catch (error93) {
|
|
40471
40871
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -40482,7 +40882,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40482
40882
|
timestamp: new Date().toISOString()
|
|
40483
40883
|
};
|
|
40484
40884
|
try {
|
|
40485
|
-
|
|
40885
|
+
fs16.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
40486
40886
|
`);
|
|
40487
40887
|
} catch (error93) {
|
|
40488
40888
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -40525,11 +40925,11 @@ async function handleSimulateCommand(directory, args) {
|
|
|
40525
40925
|
];
|
|
40526
40926
|
const report = reportLines.filter(Boolean).join(`
|
|
40527
40927
|
`);
|
|
40528
|
-
const
|
|
40529
|
-
const
|
|
40530
|
-
const reportPath =
|
|
40531
|
-
await
|
|
40532
|
-
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");
|
|
40533
40933
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
40534
40934
|
}
|
|
40535
40935
|
|
|
@@ -41011,18 +41411,18 @@ function resolveCommand(tokens) {
|
|
|
41011
41411
|
}
|
|
41012
41412
|
|
|
41013
41413
|
// src/cli/index.ts
|
|
41014
|
-
var CONFIG_DIR =
|
|
41015
|
-
var OPENCODE_CONFIG_PATH =
|
|
41016
|
-
var PLUGIN_CONFIG_PATH =
|
|
41017
|
-
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");
|
|
41018
41418
|
function ensureDir(dir) {
|
|
41019
|
-
if (!
|
|
41020
|
-
|
|
41419
|
+
if (!fs17.existsSync(dir)) {
|
|
41420
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
41021
41421
|
}
|
|
41022
41422
|
}
|
|
41023
41423
|
function loadJson(filepath) {
|
|
41024
41424
|
try {
|
|
41025
|
-
const content =
|
|
41425
|
+
const content = fs17.readFileSync(filepath, "utf-8");
|
|
41026
41426
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
41027
41427
|
return JSON.parse(stripped);
|
|
41028
41428
|
} catch {
|
|
@@ -41030,7 +41430,7 @@ function loadJson(filepath) {
|
|
|
41030
41430
|
}
|
|
41031
41431
|
}
|
|
41032
41432
|
function saveJson(filepath, data) {
|
|
41033
|
-
|
|
41433
|
+
fs17.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
41034
41434
|
`, "utf-8");
|
|
41035
41435
|
}
|
|
41036
41436
|
async function install() {
|
|
@@ -41038,7 +41438,7 @@ async function install() {
|
|
|
41038
41438
|
`);
|
|
41039
41439
|
ensureDir(CONFIG_DIR);
|
|
41040
41440
|
ensureDir(PROMPTS_DIR);
|
|
41041
|
-
const LEGACY_CONFIG_PATH =
|
|
41441
|
+
const LEGACY_CONFIG_PATH = path27.join(CONFIG_DIR, "config.json");
|
|
41042
41442
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41043
41443
|
if (!opencodeConfig) {
|
|
41044
41444
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -41063,7 +41463,7 @@ async function install() {
|
|
|
41063
41463
|
saveJson(OPENCODE_CONFIG_PATH, opencodeConfig);
|
|
41064
41464
|
console.log("\u2713 Added opencode-swarm to OpenCode plugins");
|
|
41065
41465
|
console.log("\u2713 Disabled default OpenCode agents (explore, general)");
|
|
41066
|
-
if (!
|
|
41466
|
+
if (!fs17.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41067
41467
|
const defaultConfig = {
|
|
41068
41468
|
agents: {
|
|
41069
41469
|
coder: { model: "opencode/minimax-m2.5-free" },
|
|
@@ -41106,7 +41506,7 @@ async function uninstall() {
|
|
|
41106
41506
|
`);
|
|
41107
41507
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41108
41508
|
if (!opencodeConfig) {
|
|
41109
|
-
if (
|
|
41509
|
+
if (fs17.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
41110
41510
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
41111
41511
|
return 1;
|
|
41112
41512
|
} else {
|
|
@@ -41138,13 +41538,13 @@ async function uninstall() {
|
|
|
41138
41538
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
41139
41539
|
if (process.argv.includes("--clean")) {
|
|
41140
41540
|
let cleaned = false;
|
|
41141
|
-
if (
|
|
41142
|
-
|
|
41541
|
+
if (fs17.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41542
|
+
fs17.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
41143
41543
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
41144
41544
|
cleaned = true;
|
|
41145
41545
|
}
|
|
41146
|
-
if (
|
|
41147
|
-
|
|
41546
|
+
if (fs17.existsSync(PROMPTS_DIR)) {
|
|
41547
|
+
fs17.rmSync(PROMPTS_DIR, { recursive: true });
|
|
41148
41548
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
41149
41549
|
cleaned = true;
|
|
41150
41550
|
}
|