opencode-swarm 6.43.2 → 6.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +783 -347
- package/dist/config/constants.d.ts +13 -0
- package/dist/index.js +1688 -1128
- 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/dist/tools/lint.d.ts +8 -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))
|
|
@@ -36927,17 +37327,53 @@ function detectAdditionalLinter(cwd) {
|
|
|
36927
37327
|
return "rubocop";
|
|
36928
37328
|
return null;
|
|
36929
37329
|
}
|
|
37330
|
+
function findBinInAncestors(startDir, binName) {
|
|
37331
|
+
let dir = startDir;
|
|
37332
|
+
while (true) {
|
|
37333
|
+
const candidate = path19.join(dir, "node_modules", ".bin", binName);
|
|
37334
|
+
if (fs9.existsSync(candidate))
|
|
37335
|
+
return candidate;
|
|
37336
|
+
const parent = path19.dirname(dir);
|
|
37337
|
+
if (parent === dir)
|
|
37338
|
+
break;
|
|
37339
|
+
dir = parent;
|
|
37340
|
+
}
|
|
37341
|
+
return null;
|
|
37342
|
+
}
|
|
37343
|
+
function findBinInEnvPath(binName) {
|
|
37344
|
+
const searchPath = process.env.PATH ?? "";
|
|
37345
|
+
for (const dir of searchPath.split(path19.delimiter)) {
|
|
37346
|
+
if (!dir)
|
|
37347
|
+
continue;
|
|
37348
|
+
const candidate = path19.join(dir, binName);
|
|
37349
|
+
if (fs9.existsSync(candidate))
|
|
37350
|
+
return candidate;
|
|
37351
|
+
}
|
|
37352
|
+
return null;
|
|
37353
|
+
}
|
|
36930
37354
|
async function detectAvailableLinter(directory) {
|
|
36931
|
-
const _DETECT_TIMEOUT = 2000;
|
|
36932
37355
|
if (!directory)
|
|
36933
37356
|
return null;
|
|
36934
|
-
if (!
|
|
37357
|
+
if (!fs9.existsSync(directory))
|
|
36935
37358
|
return null;
|
|
36936
37359
|
const projectDir = directory;
|
|
36937
37360
|
const isWindows = process.platform === "win32";
|
|
36938
|
-
const biomeBin = isWindows ?
|
|
36939
|
-
const eslintBin = isWindows ?
|
|
36940
|
-
|
|
37361
|
+
const biomeBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "biome.EXE") : path19.join(projectDir, "node_modules", ".bin", "biome");
|
|
37362
|
+
const eslintBin = isWindows ? path19.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path19.join(projectDir, "node_modules", ".bin", "eslint");
|
|
37363
|
+
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
37364
|
+
if (localResult)
|
|
37365
|
+
return localResult;
|
|
37366
|
+
const biomeAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
37367
|
+
const eslintAncestor = findBinInAncestors(path19.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
37368
|
+
if (biomeAncestor || eslintAncestor) {
|
|
37369
|
+
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
37370
|
+
}
|
|
37371
|
+
const pathBiome = findBinInEnvPath(isWindows ? "biome.EXE" : "biome");
|
|
37372
|
+
const pathEslint = findBinInEnvPath(isWindows ? "eslint.cmd" : "eslint");
|
|
37373
|
+
if (pathBiome || pathEslint) {
|
|
37374
|
+
return _detectAvailableLinter(projectDir, pathBiome ?? biomeBin, pathEslint ?? eslintBin);
|
|
37375
|
+
}
|
|
37376
|
+
return null;
|
|
36941
37377
|
}
|
|
36942
37378
|
async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
36943
37379
|
const DETECT_TIMEOUT = 2000;
|
|
@@ -36951,7 +37387,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36951
37387
|
const result = await Promise.race([biomeExit, timeout]);
|
|
36952
37388
|
if (result === "timeout") {
|
|
36953
37389
|
biomeProc.kill();
|
|
36954
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
37390
|
+
} else if (biomeProc.exitCode === 0 && fs9.existsSync(biomeBin)) {
|
|
36955
37391
|
return "biome";
|
|
36956
37392
|
}
|
|
36957
37393
|
} catch {}
|
|
@@ -36965,7 +37401,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36965
37401
|
const result = await Promise.race([eslintExit, timeout]);
|
|
36966
37402
|
if (result === "timeout") {
|
|
36967
37403
|
eslintProc.kill();
|
|
36968
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
37404
|
+
} else if (eslintProc.exitCode === 0 && fs9.existsSync(eslintBin)) {
|
|
36969
37405
|
return "eslint";
|
|
36970
37406
|
}
|
|
36971
37407
|
} catch {}
|
|
@@ -37135,8 +37571,8 @@ For Rust: rustup component add clippy`
|
|
|
37135
37571
|
});
|
|
37136
37572
|
|
|
37137
37573
|
// src/tools/secretscan.ts
|
|
37138
|
-
import * as
|
|
37139
|
-
import * as
|
|
37574
|
+
import * as fs10 from "fs";
|
|
37575
|
+
import * as path20 from "path";
|
|
37140
37576
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
37141
37577
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
37142
37578
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -37363,11 +37799,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
37363
37799
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
37364
37800
|
}
|
|
37365
37801
|
function loadSecretScanIgnore(scanDir) {
|
|
37366
|
-
const ignorePath =
|
|
37802
|
+
const ignorePath = path20.join(scanDir, ".secretscanignore");
|
|
37367
37803
|
try {
|
|
37368
|
-
if (!
|
|
37804
|
+
if (!fs10.existsSync(ignorePath))
|
|
37369
37805
|
return [];
|
|
37370
|
-
const content =
|
|
37806
|
+
const content = fs10.readFileSync(ignorePath, "utf8");
|
|
37371
37807
|
const patterns = [];
|
|
37372
37808
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
37373
37809
|
const line = rawLine.trim();
|
|
@@ -37386,7 +37822,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
37386
37822
|
if (exactNames.has(entry))
|
|
37387
37823
|
return true;
|
|
37388
37824
|
for (const pattern of globPatterns) {
|
|
37389
|
-
if (
|
|
37825
|
+
if (path20.matchesGlob(relPath, pattern))
|
|
37390
37826
|
return true;
|
|
37391
37827
|
}
|
|
37392
37828
|
return false;
|
|
@@ -37407,7 +37843,7 @@ function validateDirectoryInput(dir) {
|
|
|
37407
37843
|
return null;
|
|
37408
37844
|
}
|
|
37409
37845
|
function isBinaryFile(filePath, buffer) {
|
|
37410
|
-
const ext =
|
|
37846
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
37411
37847
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37412
37848
|
return true;
|
|
37413
37849
|
}
|
|
@@ -37482,11 +37918,11 @@ function createRedactedContext(line, findings) {
|
|
|
37482
37918
|
result += line.slice(lastEnd);
|
|
37483
37919
|
return result;
|
|
37484
37920
|
}
|
|
37485
|
-
var O_NOFOLLOW = process.platform !== "win32" ?
|
|
37921
|
+
var O_NOFOLLOW = process.platform !== "win32" ? fs10.constants.O_NOFOLLOW : undefined;
|
|
37486
37922
|
function scanFileForSecrets(filePath) {
|
|
37487
37923
|
const findings = [];
|
|
37488
37924
|
try {
|
|
37489
|
-
const lstat =
|
|
37925
|
+
const lstat = fs10.lstatSync(filePath);
|
|
37490
37926
|
if (lstat.isSymbolicLink()) {
|
|
37491
37927
|
return findings;
|
|
37492
37928
|
}
|
|
@@ -37495,14 +37931,14 @@ function scanFileForSecrets(filePath) {
|
|
|
37495
37931
|
}
|
|
37496
37932
|
let buffer;
|
|
37497
37933
|
if (O_NOFOLLOW !== undefined) {
|
|
37498
|
-
const fd =
|
|
37934
|
+
const fd = fs10.openSync(filePath, "r", O_NOFOLLOW);
|
|
37499
37935
|
try {
|
|
37500
|
-
buffer =
|
|
37936
|
+
buffer = fs10.readFileSync(fd);
|
|
37501
37937
|
} finally {
|
|
37502
|
-
|
|
37938
|
+
fs10.closeSync(fd);
|
|
37503
37939
|
}
|
|
37504
37940
|
} else {
|
|
37505
|
-
buffer =
|
|
37941
|
+
buffer = fs10.readFileSync(filePath);
|
|
37506
37942
|
}
|
|
37507
37943
|
if (isBinaryFile(filePath, buffer)) {
|
|
37508
37944
|
return findings;
|
|
@@ -37544,9 +37980,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
37544
37980
|
return false;
|
|
37545
37981
|
}
|
|
37546
37982
|
function isPathWithinScope(realPath, scanDir) {
|
|
37547
|
-
const resolvedScanDir =
|
|
37548
|
-
const resolvedRealPath =
|
|
37549
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
37983
|
+
const resolvedScanDir = path20.resolve(scanDir);
|
|
37984
|
+
const resolvedRealPath = path20.resolve(realPath);
|
|
37985
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path20.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
37550
37986
|
}
|
|
37551
37987
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
37552
37988
|
skippedDirs: 0,
|
|
@@ -37557,7 +37993,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37557
37993
|
const files = [];
|
|
37558
37994
|
let entries;
|
|
37559
37995
|
try {
|
|
37560
|
-
entries =
|
|
37996
|
+
entries = fs10.readdirSync(dir);
|
|
37561
37997
|
} catch {
|
|
37562
37998
|
stats.fileErrors++;
|
|
37563
37999
|
return files;
|
|
@@ -37572,15 +38008,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37572
38008
|
return a.localeCompare(b);
|
|
37573
38009
|
});
|
|
37574
38010
|
for (const entry of entries) {
|
|
37575
|
-
const fullPath =
|
|
37576
|
-
const relPath =
|
|
38011
|
+
const fullPath = path20.join(dir, entry);
|
|
38012
|
+
const relPath = path20.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
37577
38013
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
37578
38014
|
stats.skippedDirs++;
|
|
37579
38015
|
continue;
|
|
37580
38016
|
}
|
|
37581
38017
|
let lstat;
|
|
37582
38018
|
try {
|
|
37583
|
-
lstat =
|
|
38019
|
+
lstat = fs10.lstatSync(fullPath);
|
|
37584
38020
|
} catch {
|
|
37585
38021
|
stats.fileErrors++;
|
|
37586
38022
|
continue;
|
|
@@ -37592,7 +38028,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37592
38028
|
if (lstat.isDirectory()) {
|
|
37593
38029
|
let realPath;
|
|
37594
38030
|
try {
|
|
37595
|
-
realPath =
|
|
38031
|
+
realPath = fs10.realpathSync(fullPath);
|
|
37596
38032
|
} catch {
|
|
37597
38033
|
stats.fileErrors++;
|
|
37598
38034
|
continue;
|
|
@@ -37608,7 +38044,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
37608
38044
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
37609
38045
|
files.push(...subFiles);
|
|
37610
38046
|
} else if (lstat.isFile()) {
|
|
37611
|
-
const ext =
|
|
38047
|
+
const ext = path20.extname(fullPath).toLowerCase();
|
|
37612
38048
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
37613
38049
|
files.push(fullPath);
|
|
37614
38050
|
} else {
|
|
@@ -37674,8 +38110,8 @@ var secretscan = createSwarmTool({
|
|
|
37674
38110
|
}
|
|
37675
38111
|
}
|
|
37676
38112
|
try {
|
|
37677
|
-
const scanDir =
|
|
37678
|
-
if (!
|
|
38113
|
+
const scanDir = path20.resolve(directory);
|
|
38114
|
+
if (!fs10.existsSync(scanDir)) {
|
|
37679
38115
|
const errorResult = {
|
|
37680
38116
|
error: "directory not found",
|
|
37681
38117
|
scan_dir: directory,
|
|
@@ -37686,7 +38122,7 @@ var secretscan = createSwarmTool({
|
|
|
37686
38122
|
};
|
|
37687
38123
|
return JSON.stringify(errorResult, null, 2);
|
|
37688
38124
|
}
|
|
37689
|
-
const dirStat =
|
|
38125
|
+
const dirStat = fs10.statSync(scanDir);
|
|
37690
38126
|
if (!dirStat.isDirectory()) {
|
|
37691
38127
|
const errorResult = {
|
|
37692
38128
|
error: "target must be a directory, not a file",
|
|
@@ -37737,7 +38173,7 @@ var secretscan = createSwarmTool({
|
|
|
37737
38173
|
break;
|
|
37738
38174
|
const fileFindings = scanFileForSecrets(filePath);
|
|
37739
38175
|
try {
|
|
37740
|
-
const stat2 =
|
|
38176
|
+
const stat2 = fs10.statSync(filePath);
|
|
37741
38177
|
if (stat2.size > MAX_FILE_SIZE_BYTES) {
|
|
37742
38178
|
skippedFiles++;
|
|
37743
38179
|
continue;
|
|
@@ -37824,12 +38260,12 @@ async function runSecretscan(directory) {
|
|
|
37824
38260
|
}
|
|
37825
38261
|
|
|
37826
38262
|
// src/tools/test-runner.ts
|
|
37827
|
-
import * as
|
|
37828
|
-
import * as
|
|
38263
|
+
import * as fs12 from "fs";
|
|
38264
|
+
import * as path22 from "path";
|
|
37829
38265
|
|
|
37830
38266
|
// src/tools/resolve-working-directory.ts
|
|
37831
|
-
import * as
|
|
37832
|
-
import * as
|
|
38267
|
+
import * as fs11 from "fs";
|
|
38268
|
+
import * as path21 from "path";
|
|
37833
38269
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
37834
38270
|
if (workingDirectory == null || workingDirectory === "") {
|
|
37835
38271
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -37849,17 +38285,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
37849
38285
|
};
|
|
37850
38286
|
}
|
|
37851
38287
|
}
|
|
37852
|
-
const normalizedDir =
|
|
37853
|
-
const pathParts = normalizedDir.split(
|
|
38288
|
+
const normalizedDir = path21.normalize(workingDirectory);
|
|
38289
|
+
const pathParts = normalizedDir.split(path21.sep);
|
|
37854
38290
|
if (pathParts.includes("..")) {
|
|
37855
38291
|
return {
|
|
37856
38292
|
success: false,
|
|
37857
38293
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
37858
38294
|
};
|
|
37859
38295
|
}
|
|
37860
|
-
const resolvedDir =
|
|
38296
|
+
const resolvedDir = path21.resolve(normalizedDir);
|
|
37861
38297
|
try {
|
|
37862
|
-
const realPath =
|
|
38298
|
+
const realPath = fs11.realpathSync(resolvedDir);
|
|
37863
38299
|
return { success: true, directory: realPath };
|
|
37864
38300
|
} catch {
|
|
37865
38301
|
return {
|
|
@@ -37940,19 +38376,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
37940
38376
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
37941
38377
|
}
|
|
37942
38378
|
function detectGoTest(cwd) {
|
|
37943
|
-
return
|
|
38379
|
+
return fs12.existsSync(path22.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
37944
38380
|
}
|
|
37945
38381
|
function detectJavaMaven(cwd) {
|
|
37946
|
-
return
|
|
38382
|
+
return fs12.existsSync(path22.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
37947
38383
|
}
|
|
37948
38384
|
function detectGradle(cwd) {
|
|
37949
|
-
const hasBuildFile =
|
|
37950
|
-
const hasGradlew =
|
|
38385
|
+
const hasBuildFile = fs12.existsSync(path22.join(cwd, "build.gradle")) || fs12.existsSync(path22.join(cwd, "build.gradle.kts"));
|
|
38386
|
+
const hasGradlew = fs12.existsSync(path22.join(cwd, "gradlew")) || fs12.existsSync(path22.join(cwd, "gradlew.bat"));
|
|
37951
38387
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
37952
38388
|
}
|
|
37953
38389
|
function detectDotnetTest(cwd) {
|
|
37954
38390
|
try {
|
|
37955
|
-
const files =
|
|
38391
|
+
const files = fs12.readdirSync(cwd);
|
|
37956
38392
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
37957
38393
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
37958
38394
|
} catch {
|
|
@@ -37960,32 +38396,32 @@ function detectDotnetTest(cwd) {
|
|
|
37960
38396
|
}
|
|
37961
38397
|
}
|
|
37962
38398
|
function detectCTest(cwd) {
|
|
37963
|
-
const hasSource =
|
|
37964
|
-
const hasBuildCache =
|
|
38399
|
+
const hasSource = fs12.existsSync(path22.join(cwd, "CMakeLists.txt"));
|
|
38400
|
+
const hasBuildCache = fs12.existsSync(path22.join(cwd, "CMakeCache.txt")) || fs12.existsSync(path22.join(cwd, "build", "CMakeCache.txt"));
|
|
37965
38401
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
37966
38402
|
}
|
|
37967
38403
|
function detectSwiftTest(cwd) {
|
|
37968
|
-
return
|
|
38404
|
+
return fs12.existsSync(path22.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
37969
38405
|
}
|
|
37970
38406
|
function detectDartTest(cwd) {
|
|
37971
|
-
return
|
|
38407
|
+
return fs12.existsSync(path22.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
37972
38408
|
}
|
|
37973
38409
|
function detectRSpec(cwd) {
|
|
37974
|
-
const hasRSpecFile =
|
|
37975
|
-
const hasGemfile =
|
|
37976
|
-
const hasSpecDir =
|
|
38410
|
+
const hasRSpecFile = fs12.existsSync(path22.join(cwd, ".rspec"));
|
|
38411
|
+
const hasGemfile = fs12.existsSync(path22.join(cwd, "Gemfile"));
|
|
38412
|
+
const hasSpecDir = fs12.existsSync(path22.join(cwd, "spec"));
|
|
37977
38413
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
37978
38414
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
37979
38415
|
}
|
|
37980
38416
|
function detectMinitest(cwd) {
|
|
37981
|
-
return
|
|
38417
|
+
return fs12.existsSync(path22.join(cwd, "test")) && (fs12.existsSync(path22.join(cwd, "Gemfile")) || fs12.existsSync(path22.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
37982
38418
|
}
|
|
37983
38419
|
async function detectTestFramework(cwd) {
|
|
37984
38420
|
const baseDir = cwd;
|
|
37985
38421
|
try {
|
|
37986
|
-
const packageJsonPath =
|
|
37987
|
-
if (
|
|
37988
|
-
const content =
|
|
38422
|
+
const packageJsonPath = path22.join(baseDir, "package.json");
|
|
38423
|
+
if (fs12.existsSync(packageJsonPath)) {
|
|
38424
|
+
const content = fs12.readFileSync(packageJsonPath, "utf-8");
|
|
37989
38425
|
const pkg = JSON.parse(content);
|
|
37990
38426
|
const _deps = pkg.dependencies || {};
|
|
37991
38427
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -38004,38 +38440,38 @@ async function detectTestFramework(cwd) {
|
|
|
38004
38440
|
return "jest";
|
|
38005
38441
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
38006
38442
|
return "mocha";
|
|
38007
|
-
if (
|
|
38443
|
+
if (fs12.existsSync(path22.join(baseDir, "bun.lockb")) || fs12.existsSync(path22.join(baseDir, "bun.lock"))) {
|
|
38008
38444
|
if (scripts.test?.includes("bun"))
|
|
38009
38445
|
return "bun";
|
|
38010
38446
|
}
|
|
38011
38447
|
}
|
|
38012
38448
|
} catch {}
|
|
38013
38449
|
try {
|
|
38014
|
-
const pyprojectTomlPath =
|
|
38015
|
-
const setupCfgPath =
|
|
38016
|
-
const requirementsTxtPath =
|
|
38017
|
-
if (
|
|
38018
|
-
const content =
|
|
38450
|
+
const pyprojectTomlPath = path22.join(baseDir, "pyproject.toml");
|
|
38451
|
+
const setupCfgPath = path22.join(baseDir, "setup.cfg");
|
|
38452
|
+
const requirementsTxtPath = path22.join(baseDir, "requirements.txt");
|
|
38453
|
+
if (fs12.existsSync(pyprojectTomlPath)) {
|
|
38454
|
+
const content = fs12.readFileSync(pyprojectTomlPath, "utf-8");
|
|
38019
38455
|
if (content.includes("[tool.pytest"))
|
|
38020
38456
|
return "pytest";
|
|
38021
38457
|
if (content.includes("pytest"))
|
|
38022
38458
|
return "pytest";
|
|
38023
38459
|
}
|
|
38024
|
-
if (
|
|
38025
|
-
const content =
|
|
38460
|
+
if (fs12.existsSync(setupCfgPath)) {
|
|
38461
|
+
const content = fs12.readFileSync(setupCfgPath, "utf-8");
|
|
38026
38462
|
if (content.includes("[pytest]"))
|
|
38027
38463
|
return "pytest";
|
|
38028
38464
|
}
|
|
38029
|
-
if (
|
|
38030
|
-
const content =
|
|
38465
|
+
if (fs12.existsSync(requirementsTxtPath)) {
|
|
38466
|
+
const content = fs12.readFileSync(requirementsTxtPath, "utf-8");
|
|
38031
38467
|
if (content.includes("pytest"))
|
|
38032
38468
|
return "pytest";
|
|
38033
38469
|
}
|
|
38034
38470
|
} catch {}
|
|
38035
38471
|
try {
|
|
38036
|
-
const cargoTomlPath =
|
|
38037
|
-
if (
|
|
38038
|
-
const content =
|
|
38472
|
+
const cargoTomlPath = path22.join(baseDir, "Cargo.toml");
|
|
38473
|
+
if (fs12.existsSync(cargoTomlPath)) {
|
|
38474
|
+
const content = fs12.readFileSync(cargoTomlPath, "utf-8");
|
|
38039
38475
|
if (content.includes("[dev-dependencies]")) {
|
|
38040
38476
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
38041
38477
|
return "cargo";
|
|
@@ -38044,10 +38480,10 @@ async function detectTestFramework(cwd) {
|
|
|
38044
38480
|
}
|
|
38045
38481
|
} catch {}
|
|
38046
38482
|
try {
|
|
38047
|
-
const pesterConfigPath =
|
|
38048
|
-
const pesterConfigJsonPath =
|
|
38049
|
-
const pesterPs1Path =
|
|
38050
|
-
if (
|
|
38483
|
+
const pesterConfigPath = path22.join(baseDir, "pester.config.ps1");
|
|
38484
|
+
const pesterConfigJsonPath = path22.join(baseDir, "pester.config.ps1.json");
|
|
38485
|
+
const pesterPs1Path = path22.join(baseDir, "tests.ps1");
|
|
38486
|
+
if (fs12.existsSync(pesterConfigPath) || fs12.existsSync(pesterConfigJsonPath) || fs12.existsSync(pesterPs1Path)) {
|
|
38051
38487
|
return "pester";
|
|
38052
38488
|
}
|
|
38053
38489
|
} catch {}
|
|
@@ -38098,8 +38534,8 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38098
38534
|
const testFiles = [];
|
|
38099
38535
|
for (const file3 of sourceFiles) {
|
|
38100
38536
|
const normalizedPath = file3.replace(/\\/g, "/");
|
|
38101
|
-
const basename4 =
|
|
38102
|
-
const
|
|
38537
|
+
const basename4 = path22.basename(file3);
|
|
38538
|
+
const dirname10 = path22.dirname(file3);
|
|
38103
38539
|
if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
38104
38540
|
if (!testFiles.includes(file3)) {
|
|
38105
38541
|
testFiles.push(file3);
|
|
@@ -38108,16 +38544,16 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
38108
38544
|
}
|
|
38109
38545
|
for (const _pattern of TEST_PATTERNS) {
|
|
38110
38546
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
38111
|
-
const ext =
|
|
38547
|
+
const ext = path22.extname(basename4);
|
|
38112
38548
|
const possibleTestFiles = [
|
|
38113
|
-
|
|
38114
|
-
|
|
38115
|
-
|
|
38116
|
-
|
|
38117
|
-
|
|
38549
|
+
path22.join(dirname10, `${nameWithoutExt}.spec${ext}`),
|
|
38550
|
+
path22.join(dirname10, `${nameWithoutExt}.test${ext}`),
|
|
38551
|
+
path22.join(dirname10, "__tests__", `${nameWithoutExt}${ext}`),
|
|
38552
|
+
path22.join(dirname10, "tests", `${nameWithoutExt}${ext}`),
|
|
38553
|
+
path22.join(dirname10, "test", `${nameWithoutExt}${ext}`)
|
|
38118
38554
|
];
|
|
38119
38555
|
for (const testFile of possibleTestFiles) {
|
|
38120
|
-
if (
|
|
38556
|
+
if (fs12.existsSync(testFile) && !testFiles.includes(testFile)) {
|
|
38121
38557
|
testFiles.push(testFile);
|
|
38122
38558
|
}
|
|
38123
38559
|
}
|
|
@@ -38133,8 +38569,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38133
38569
|
}
|
|
38134
38570
|
for (const testFile of candidateTestFiles) {
|
|
38135
38571
|
try {
|
|
38136
|
-
const content =
|
|
38137
|
-
const testDir =
|
|
38572
|
+
const content = fs12.readFileSync(testFile, "utf-8");
|
|
38573
|
+
const testDir = path22.dirname(testFile);
|
|
38138
38574
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
38139
38575
|
let match;
|
|
38140
38576
|
match = importRegex.exec(content);
|
|
@@ -38142,8 +38578,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38142
38578
|
const importPath = match[1];
|
|
38143
38579
|
let resolvedImport;
|
|
38144
38580
|
if (importPath.startsWith(".")) {
|
|
38145
|
-
resolvedImport =
|
|
38146
|
-
const existingExt =
|
|
38581
|
+
resolvedImport = path22.resolve(testDir, importPath);
|
|
38582
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38147
38583
|
if (!existingExt) {
|
|
38148
38584
|
for (const extToTry of [
|
|
38149
38585
|
".ts",
|
|
@@ -38154,7 +38590,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38154
38590
|
".cjs"
|
|
38155
38591
|
]) {
|
|
38156
38592
|
const withExt = resolvedImport + extToTry;
|
|
38157
|
-
if (sourceFiles.includes(withExt) ||
|
|
38593
|
+
if (sourceFiles.includes(withExt) || fs12.existsSync(withExt)) {
|
|
38158
38594
|
resolvedImport = withExt;
|
|
38159
38595
|
break;
|
|
38160
38596
|
}
|
|
@@ -38163,12 +38599,12 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38163
38599
|
} else {
|
|
38164
38600
|
continue;
|
|
38165
38601
|
}
|
|
38166
|
-
const importBasename =
|
|
38167
|
-
const importDir =
|
|
38602
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38603
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38168
38604
|
for (const sourceFile of sourceFiles) {
|
|
38169
|
-
const sourceDir =
|
|
38170
|
-
const sourceBasename =
|
|
38171
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38605
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38606
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38607
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38172
38608
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38173
38609
|
if (!testFiles.includes(testFile)) {
|
|
38174
38610
|
testFiles.push(testFile);
|
|
@@ -38183,8 +38619,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38183
38619
|
while (match !== null) {
|
|
38184
38620
|
const importPath = match[1];
|
|
38185
38621
|
if (importPath.startsWith(".")) {
|
|
38186
|
-
let resolvedImport =
|
|
38187
|
-
const existingExt =
|
|
38622
|
+
let resolvedImport = path22.resolve(testDir, importPath);
|
|
38623
|
+
const existingExt = path22.extname(resolvedImport);
|
|
38188
38624
|
if (!existingExt) {
|
|
38189
38625
|
for (const extToTry of [
|
|
38190
38626
|
".ts",
|
|
@@ -38195,18 +38631,18 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
38195
38631
|
".cjs"
|
|
38196
38632
|
]) {
|
|
38197
38633
|
const withExt = resolvedImport + extToTry;
|
|
38198
|
-
if (sourceFiles.includes(withExt) ||
|
|
38634
|
+
if (sourceFiles.includes(withExt) || fs12.existsSync(withExt)) {
|
|
38199
38635
|
resolvedImport = withExt;
|
|
38200
38636
|
break;
|
|
38201
38637
|
}
|
|
38202
38638
|
}
|
|
38203
38639
|
}
|
|
38204
|
-
const importDir =
|
|
38205
|
-
const importBasename =
|
|
38640
|
+
const importDir = path22.dirname(resolvedImport);
|
|
38641
|
+
const importBasename = path22.basename(resolvedImport, path22.extname(resolvedImport));
|
|
38206
38642
|
for (const sourceFile of sourceFiles) {
|
|
38207
|
-
const sourceDir =
|
|
38208
|
-
const sourceBasename =
|
|
38209
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
38643
|
+
const sourceDir = path22.dirname(sourceFile);
|
|
38644
|
+
const sourceBasename = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
38645
|
+
const isRelatedDir = importDir === sourceDir || importDir === path22.join(sourceDir, "__tests__") || importDir === path22.join(sourceDir, "tests") || importDir === path22.join(sourceDir, "test");
|
|
38210
38646
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
38211
38647
|
if (!testFiles.includes(testFile)) {
|
|
38212
38648
|
testFiles.push(testFile);
|
|
@@ -38291,8 +38727,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38291
38727
|
return ["mvn", "test"];
|
|
38292
38728
|
case "gradle": {
|
|
38293
38729
|
const isWindows = process.platform === "win32";
|
|
38294
|
-
const hasGradlewBat =
|
|
38295
|
-
const hasGradlew =
|
|
38730
|
+
const hasGradlewBat = fs12.existsSync(path22.join(baseDir, "gradlew.bat"));
|
|
38731
|
+
const hasGradlew = fs12.existsSync(path22.join(baseDir, "gradlew"));
|
|
38296
38732
|
if (hasGradlewBat && isWindows)
|
|
38297
38733
|
return ["gradlew.bat", "test"];
|
|
38298
38734
|
if (hasGradlew)
|
|
@@ -38309,7 +38745,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
38309
38745
|
"cmake-build-release",
|
|
38310
38746
|
"out"
|
|
38311
38747
|
];
|
|
38312
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
38748
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs12.existsSync(path22.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
38313
38749
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
38314
38750
|
}
|
|
38315
38751
|
case "swift-test":
|
|
@@ -38851,7 +39287,7 @@ var test_runner = createSwarmTool({
|
|
|
38851
39287
|
let effectiveScope = scope;
|
|
38852
39288
|
if (scope === "all") {} else if (scope === "convention") {
|
|
38853
39289
|
const sourceFiles = args.files.filter((f) => {
|
|
38854
|
-
const ext =
|
|
39290
|
+
const ext = path22.extname(f).toLowerCase();
|
|
38855
39291
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38856
39292
|
});
|
|
38857
39293
|
if (sourceFiles.length === 0) {
|
|
@@ -38867,7 +39303,7 @@ var test_runner = createSwarmTool({
|
|
|
38867
39303
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
38868
39304
|
} else if (scope === "graph") {
|
|
38869
39305
|
const sourceFiles = args.files.filter((f) => {
|
|
38870
|
-
const ext =
|
|
39306
|
+
const ext = path22.extname(f).toLowerCase();
|
|
38871
39307
|
return SOURCE_EXTENSIONS.has(ext);
|
|
38872
39308
|
});
|
|
38873
39309
|
if (sourceFiles.length === 0) {
|
|
@@ -38938,8 +39374,8 @@ function validateDirectoryPath(dir) {
|
|
|
38938
39374
|
if (dir.includes("..")) {
|
|
38939
39375
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
38940
39376
|
}
|
|
38941
|
-
const normalized =
|
|
38942
|
-
const absolutePath =
|
|
39377
|
+
const normalized = path23.normalize(dir);
|
|
39378
|
+
const absolutePath = path23.isAbsolute(normalized) ? normalized : path23.resolve(normalized);
|
|
38943
39379
|
return absolutePath;
|
|
38944
39380
|
}
|
|
38945
39381
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -38962,9 +39398,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
38962
39398
|
}
|
|
38963
39399
|
function getPackageVersion(dir) {
|
|
38964
39400
|
try {
|
|
38965
|
-
const packagePath =
|
|
38966
|
-
if (
|
|
38967
|
-
const content =
|
|
39401
|
+
const packagePath = path23.join(dir, "package.json");
|
|
39402
|
+
if (fs13.existsSync(packagePath)) {
|
|
39403
|
+
const content = fs13.readFileSync(packagePath, "utf-8");
|
|
38968
39404
|
const pkg = JSON.parse(content);
|
|
38969
39405
|
return pkg.version ?? null;
|
|
38970
39406
|
}
|
|
@@ -38973,9 +39409,9 @@ function getPackageVersion(dir) {
|
|
|
38973
39409
|
}
|
|
38974
39410
|
function getChangelogVersion(dir) {
|
|
38975
39411
|
try {
|
|
38976
|
-
const changelogPath =
|
|
38977
|
-
if (
|
|
38978
|
-
const content =
|
|
39412
|
+
const changelogPath = path23.join(dir, "CHANGELOG.md");
|
|
39413
|
+
if (fs13.existsSync(changelogPath)) {
|
|
39414
|
+
const content = fs13.readFileSync(changelogPath, "utf-8");
|
|
38979
39415
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
38980
39416
|
if (match) {
|
|
38981
39417
|
return match[1];
|
|
@@ -38987,10 +39423,10 @@ function getChangelogVersion(dir) {
|
|
|
38987
39423
|
function getVersionFileVersion(dir) {
|
|
38988
39424
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
38989
39425
|
for (const file3 of possibleFiles) {
|
|
38990
|
-
const filePath =
|
|
38991
|
-
if (
|
|
39426
|
+
const filePath = path23.join(dir, file3);
|
|
39427
|
+
if (fs13.existsSync(filePath)) {
|
|
38992
39428
|
try {
|
|
38993
|
-
const content =
|
|
39429
|
+
const content = fs13.readFileSync(filePath, "utf-8").trim();
|
|
38994
39430
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
38995
39431
|
if (match) {
|
|
38996
39432
|
return match[1];
|
|
@@ -39534,7 +39970,7 @@ async function handlePromoteCommand(directory, args) {
|
|
|
39534
39970
|
}
|
|
39535
39971
|
|
|
39536
39972
|
// src/commands/reset.ts
|
|
39537
|
-
import * as
|
|
39973
|
+
import * as fs14 from "fs";
|
|
39538
39974
|
|
|
39539
39975
|
// src/background/manager.ts
|
|
39540
39976
|
init_utils();
|
|
@@ -40235,8 +40671,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40235
40671
|
for (const filename of filesToReset) {
|
|
40236
40672
|
try {
|
|
40237
40673
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
40238
|
-
if (
|
|
40239
|
-
|
|
40674
|
+
if (fs14.existsSync(resolvedPath)) {
|
|
40675
|
+
fs14.unlinkSync(resolvedPath);
|
|
40240
40676
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
40241
40677
|
} else {
|
|
40242
40678
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -40253,8 +40689,8 @@ async function handleResetCommand(directory, args) {
|
|
|
40253
40689
|
}
|
|
40254
40690
|
try {
|
|
40255
40691
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
40256
|
-
if (
|
|
40257
|
-
|
|
40692
|
+
if (fs14.existsSync(summariesPath)) {
|
|
40693
|
+
fs14.rmSync(summariesPath, { recursive: true, force: true });
|
|
40258
40694
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
40259
40695
|
} else {
|
|
40260
40696
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -40274,14 +40710,14 @@ async function handleResetCommand(directory, args) {
|
|
|
40274
40710
|
|
|
40275
40711
|
// src/commands/reset-session.ts
|
|
40276
40712
|
init_utils2();
|
|
40277
|
-
import * as
|
|
40278
|
-
import * as
|
|
40713
|
+
import * as fs15 from "fs";
|
|
40714
|
+
import * as path24 from "path";
|
|
40279
40715
|
async function handleResetSessionCommand(directory, _args) {
|
|
40280
40716
|
const results = [];
|
|
40281
40717
|
try {
|
|
40282
40718
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
40283
|
-
if (
|
|
40284
|
-
|
|
40719
|
+
if (fs15.existsSync(statePath)) {
|
|
40720
|
+
fs15.unlinkSync(statePath);
|
|
40285
40721
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
40286
40722
|
} else {
|
|
40287
40723
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -40290,15 +40726,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40290
40726
|
results.push("\u274C Failed to delete state.json");
|
|
40291
40727
|
}
|
|
40292
40728
|
try {
|
|
40293
|
-
const sessionDir =
|
|
40294
|
-
if (
|
|
40295
|
-
const files =
|
|
40729
|
+
const sessionDir = path24.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
40730
|
+
if (fs15.existsSync(sessionDir)) {
|
|
40731
|
+
const files = fs15.readdirSync(sessionDir);
|
|
40296
40732
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
40297
40733
|
let deletedCount = 0;
|
|
40298
40734
|
for (const file3 of otherFiles) {
|
|
40299
|
-
const filePath =
|
|
40300
|
-
if (
|
|
40301
|
-
|
|
40735
|
+
const filePath = path24.join(sessionDir, file3);
|
|
40736
|
+
if (fs15.lstatSync(filePath).isFile()) {
|
|
40737
|
+
fs15.unlinkSync(filePath);
|
|
40302
40738
|
deletedCount++;
|
|
40303
40739
|
}
|
|
40304
40740
|
}
|
|
@@ -40326,7 +40762,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
40326
40762
|
// src/summaries/manager.ts
|
|
40327
40763
|
init_utils2();
|
|
40328
40764
|
init_utils();
|
|
40329
|
-
import * as
|
|
40765
|
+
import * as path25 from "path";
|
|
40330
40766
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
40331
40767
|
function sanitizeSummaryId(id) {
|
|
40332
40768
|
if (!id || id.length === 0) {
|
|
@@ -40350,7 +40786,7 @@ function sanitizeSummaryId(id) {
|
|
|
40350
40786
|
}
|
|
40351
40787
|
async function loadFullOutput(directory, id) {
|
|
40352
40788
|
const sanitizedId = sanitizeSummaryId(id);
|
|
40353
|
-
const relativePath =
|
|
40789
|
+
const relativePath = path25.join("summaries", `${sanitizedId}.json`);
|
|
40354
40790
|
validateSwarmPath(directory, relativePath);
|
|
40355
40791
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
40356
40792
|
if (content === null) {
|
|
@@ -40403,18 +40839,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
40403
40839
|
|
|
40404
40840
|
// src/commands/rollback.ts
|
|
40405
40841
|
init_utils2();
|
|
40406
|
-
import * as
|
|
40407
|
-
import * as
|
|
40842
|
+
import * as fs16 from "fs";
|
|
40843
|
+
import * as path26 from "path";
|
|
40408
40844
|
async function handleRollbackCommand(directory, args) {
|
|
40409
40845
|
const phaseArg = args[0];
|
|
40410
40846
|
if (!phaseArg) {
|
|
40411
40847
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40412
|
-
if (!
|
|
40848
|
+
if (!fs16.existsSync(manifestPath2)) {
|
|
40413
40849
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
40414
40850
|
}
|
|
40415
40851
|
let manifest2;
|
|
40416
40852
|
try {
|
|
40417
|
-
manifest2 = JSON.parse(
|
|
40853
|
+
manifest2 = JSON.parse(fs16.readFileSync(manifestPath2, "utf-8"));
|
|
40418
40854
|
} catch {
|
|
40419
40855
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
40420
40856
|
}
|
|
@@ -40436,12 +40872,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40436
40872
|
return "Error: Phase number must be a positive integer.";
|
|
40437
40873
|
}
|
|
40438
40874
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
40439
|
-
if (!
|
|
40875
|
+
if (!fs16.existsSync(manifestPath)) {
|
|
40440
40876
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
40441
40877
|
}
|
|
40442
40878
|
let manifest;
|
|
40443
40879
|
try {
|
|
40444
|
-
manifest = JSON.parse(
|
|
40880
|
+
manifest = JSON.parse(fs16.readFileSync(manifestPath, "utf-8"));
|
|
40445
40881
|
} catch {
|
|
40446
40882
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
40447
40883
|
}
|
|
@@ -40451,10 +40887,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40451
40887
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
40452
40888
|
}
|
|
40453
40889
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
40454
|
-
if (!
|
|
40890
|
+
if (!fs16.existsSync(checkpointDir)) {
|
|
40455
40891
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
40456
40892
|
}
|
|
40457
|
-
const checkpointFiles =
|
|
40893
|
+
const checkpointFiles = fs16.readdirSync(checkpointDir);
|
|
40458
40894
|
if (checkpointFiles.length === 0) {
|
|
40459
40895
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
40460
40896
|
}
|
|
@@ -40462,10 +40898,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40462
40898
|
const successes = [];
|
|
40463
40899
|
const failures = [];
|
|
40464
40900
|
for (const file3 of checkpointFiles) {
|
|
40465
|
-
const src =
|
|
40466
|
-
const dest =
|
|
40901
|
+
const src = path26.join(checkpointDir, file3);
|
|
40902
|
+
const dest = path26.join(swarmDir, file3);
|
|
40467
40903
|
try {
|
|
40468
|
-
|
|
40904
|
+
fs16.cpSync(src, dest, { recursive: true, force: true });
|
|
40469
40905
|
successes.push(file3);
|
|
40470
40906
|
} catch (error93) {
|
|
40471
40907
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -40482,7 +40918,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
40482
40918
|
timestamp: new Date().toISOString()
|
|
40483
40919
|
};
|
|
40484
40920
|
try {
|
|
40485
|
-
|
|
40921
|
+
fs16.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
40486
40922
|
`);
|
|
40487
40923
|
} catch (error93) {
|
|
40488
40924
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -40525,11 +40961,11 @@ async function handleSimulateCommand(directory, args) {
|
|
|
40525
40961
|
];
|
|
40526
40962
|
const report = reportLines.filter(Boolean).join(`
|
|
40527
40963
|
`);
|
|
40528
|
-
const
|
|
40529
|
-
const
|
|
40530
|
-
const reportPath =
|
|
40531
|
-
await
|
|
40532
|
-
await
|
|
40964
|
+
const fs17 = await import("fs/promises");
|
|
40965
|
+
const path27 = await import("path");
|
|
40966
|
+
const reportPath = path27.join(directory, ".swarm", "simulate-report.md");
|
|
40967
|
+
await fs17.mkdir(path27.dirname(reportPath), { recursive: true });
|
|
40968
|
+
await fs17.writeFile(reportPath, report, "utf-8");
|
|
40533
40969
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
40534
40970
|
}
|
|
40535
40971
|
|
|
@@ -41011,18 +41447,18 @@ function resolveCommand(tokens) {
|
|
|
41011
41447
|
}
|
|
41012
41448
|
|
|
41013
41449
|
// src/cli/index.ts
|
|
41014
|
-
var CONFIG_DIR =
|
|
41015
|
-
var OPENCODE_CONFIG_PATH =
|
|
41016
|
-
var PLUGIN_CONFIG_PATH =
|
|
41017
|
-
var PROMPTS_DIR =
|
|
41450
|
+
var CONFIG_DIR = path27.join(process.env.XDG_CONFIG_HOME || path27.join(os5.homedir(), ".config"), "opencode");
|
|
41451
|
+
var OPENCODE_CONFIG_PATH = path27.join(CONFIG_DIR, "opencode.json");
|
|
41452
|
+
var PLUGIN_CONFIG_PATH = path27.join(CONFIG_DIR, "opencode-swarm.json");
|
|
41453
|
+
var PROMPTS_DIR = path27.join(CONFIG_DIR, "opencode-swarm");
|
|
41018
41454
|
function ensureDir(dir) {
|
|
41019
|
-
if (!
|
|
41020
|
-
|
|
41455
|
+
if (!fs17.existsSync(dir)) {
|
|
41456
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
41021
41457
|
}
|
|
41022
41458
|
}
|
|
41023
41459
|
function loadJson(filepath) {
|
|
41024
41460
|
try {
|
|
41025
|
-
const content =
|
|
41461
|
+
const content = fs17.readFileSync(filepath, "utf-8");
|
|
41026
41462
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
41027
41463
|
return JSON.parse(stripped);
|
|
41028
41464
|
} catch {
|
|
@@ -41030,7 +41466,7 @@ function loadJson(filepath) {
|
|
|
41030
41466
|
}
|
|
41031
41467
|
}
|
|
41032
41468
|
function saveJson(filepath, data) {
|
|
41033
|
-
|
|
41469
|
+
fs17.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
41034
41470
|
`, "utf-8");
|
|
41035
41471
|
}
|
|
41036
41472
|
async function install() {
|
|
@@ -41038,7 +41474,7 @@ async function install() {
|
|
|
41038
41474
|
`);
|
|
41039
41475
|
ensureDir(CONFIG_DIR);
|
|
41040
41476
|
ensureDir(PROMPTS_DIR);
|
|
41041
|
-
const LEGACY_CONFIG_PATH =
|
|
41477
|
+
const LEGACY_CONFIG_PATH = path27.join(CONFIG_DIR, "config.json");
|
|
41042
41478
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41043
41479
|
if (!opencodeConfig) {
|
|
41044
41480
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -41063,7 +41499,7 @@ async function install() {
|
|
|
41063
41499
|
saveJson(OPENCODE_CONFIG_PATH, opencodeConfig);
|
|
41064
41500
|
console.log("\u2713 Added opencode-swarm to OpenCode plugins");
|
|
41065
41501
|
console.log("\u2713 Disabled default OpenCode agents (explore, general)");
|
|
41066
|
-
if (!
|
|
41502
|
+
if (!fs17.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41067
41503
|
const defaultConfig = {
|
|
41068
41504
|
agents: {
|
|
41069
41505
|
coder: { model: "opencode/minimax-m2.5-free" },
|
|
@@ -41106,7 +41542,7 @@ async function uninstall() {
|
|
|
41106
41542
|
`);
|
|
41107
41543
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
41108
41544
|
if (!opencodeConfig) {
|
|
41109
|
-
if (
|
|
41545
|
+
if (fs17.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
41110
41546
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
41111
41547
|
return 1;
|
|
41112
41548
|
} else {
|
|
@@ -41138,13 +41574,13 @@ async function uninstall() {
|
|
|
41138
41574
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
41139
41575
|
if (process.argv.includes("--clean")) {
|
|
41140
41576
|
let cleaned = false;
|
|
41141
|
-
if (
|
|
41142
|
-
|
|
41577
|
+
if (fs17.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
41578
|
+
fs17.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
41143
41579
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
41144
41580
|
cleaned = true;
|
|
41145
41581
|
}
|
|
41146
|
-
if (
|
|
41147
|
-
|
|
41582
|
+
if (fs17.existsSync(PROMPTS_DIR)) {
|
|
41583
|
+
fs17.rmSync(PROMPTS_DIR, { recursive: true });
|
|
41148
41584
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
41149
41585
|
cleaned = true;
|
|
41150
41586
|
}
|