opencode-swarm 7.87.0 → 7.87.2
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/{evidence-summary-service-gg5m9z57.js → evidence-summary-service-5me91eq8.js} +1 -1
- package/dist/cli/{guardrail-explain-wb1cj312.js → guardrail-explain-9fngqx80.js} +3 -3
- package/dist/cli/{index-wv2yj8ka.js → index-6qkwgdsg.js} +571 -172
- package/dist/cli/{index-89xjr3h4.js → index-8y7qetpg.js} +48 -19
- package/dist/cli/{index-f13d3b69.js → index-f41fa3f0.js} +1 -1
- package/dist/cli/{index-0m44n5qv.js → index-gjdq4na6.js} +3 -3
- package/dist/cli/index.js +2 -2
- package/dist/commands/close.d.ts +6 -0
- package/dist/git/branch.d.ts +5 -0
- package/dist/git/pr.d.ts +18 -0
- package/dist/index.js +835 -366
- package/dist/memory/scoring.d.ts +18 -0
- package/dist/memory/sqlite-provider.d.ts +10 -0
- package/dist/tools/checkpoint.d.ts +1 -0
- package/dist/utils/transient-retry.d.ts +30 -0
- package/package.json +1 -1
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
stripKnownSwarmPrefix
|
|
61
61
|
} from "./index-q9h0wb04.js";
|
|
62
62
|
import {
|
|
63
|
+
MAX_TRANSIENT_RETRIES,
|
|
63
64
|
PlanSchema,
|
|
64
65
|
RetrospectiveEvidenceSchema,
|
|
65
66
|
appendLedgerEvent,
|
|
@@ -81,6 +82,7 @@ import {
|
|
|
81
82
|
isGlobalFile,
|
|
82
83
|
isProtectedPath,
|
|
83
84
|
isStateUnreadable,
|
|
85
|
+
isTransientSpawnError,
|
|
84
86
|
isValidEvidenceType,
|
|
85
87
|
listEvidenceTaskIds,
|
|
86
88
|
loadEpicSessionState,
|
|
@@ -97,9 +99,10 @@ import {
|
|
|
97
99
|
sanitizeTaskId,
|
|
98
100
|
saveEvidence,
|
|
99
101
|
savePlan,
|
|
102
|
+
transientBackoff,
|
|
100
103
|
validateProjectRoot,
|
|
101
104
|
writeProjectedSpecSync
|
|
102
|
-
} from "./index-
|
|
105
|
+
} from "./index-8y7qetpg.js";
|
|
103
106
|
import {
|
|
104
107
|
_internals as _internals2,
|
|
105
108
|
_internals1 as _internals3,
|
|
@@ -894,7 +897,7 @@ var init_executor = __esm(() => {
|
|
|
894
897
|
// package.json
|
|
895
898
|
var package_default = {
|
|
896
899
|
name: "opencode-swarm",
|
|
897
|
-
version: "7.87.
|
|
900
|
+
version: "7.87.2",
|
|
898
901
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
899
902
|
main: "dist/index.js",
|
|
900
903
|
types: "dist/index.d.ts",
|
|
@@ -5231,6 +5234,7 @@ function createSwarmTool(opts) {
|
|
|
5231
5234
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
5232
5235
|
var MAX_LABEL_LENGTH = 100;
|
|
5233
5236
|
var GIT_TIMEOUT_MS = 30000;
|
|
5237
|
+
var GIT_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
|
|
5234
5238
|
var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
5235
5239
|
var SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
5236
5240
|
var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/;
|
|
@@ -5305,20 +5309,31 @@ function writeCheckpointLog(log2, directory) {
|
|
|
5305
5309
|
fs7.renameSync(tempPath, logPath);
|
|
5306
5310
|
}
|
|
5307
5311
|
function gitExec(args, cwd) {
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5312
|
+
for (let attempt = 0;attempt < MAX_TRANSIENT_RETRIES; attempt++) {
|
|
5313
|
+
const result = child_process.spawnSync("git", args, {
|
|
5314
|
+
cwd,
|
|
5315
|
+
encoding: "utf-8",
|
|
5316
|
+
timeout: GIT_TIMEOUT_MS,
|
|
5317
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5318
|
+
windowsHide: true,
|
|
5319
|
+
maxBuffer: GIT_MAX_BUFFER_BYTES
|
|
5320
|
+
});
|
|
5321
|
+
if (result.error) {
|
|
5322
|
+
const code = result.error.code;
|
|
5323
|
+
const message = result.error.message ?? "";
|
|
5324
|
+
const isTransient = isTransientSpawnError(result.error) || /ETIMEDOUT|timed out/i.test(message);
|
|
5325
|
+
if (!isTransient || attempt >= MAX_TRANSIENT_RETRIES - 1) {
|
|
5326
|
+
throw new Error(`git failed to start: ${code ?? "unknown"} \u2014 ${message}`);
|
|
5327
|
+
}
|
|
5328
|
+
transientBackoff(attempt);
|
|
5329
|
+
continue;
|
|
5330
|
+
}
|
|
5331
|
+
if (result.status === 0) {
|
|
5332
|
+
return result.stdout ?? "";
|
|
5333
|
+
}
|
|
5319
5334
|
throw new Error(result.stderr?.trim() || `git exited with code ${result.status}`);
|
|
5320
5335
|
}
|
|
5321
|
-
|
|
5336
|
+
throw new Error("git command failed after transient retries");
|
|
5322
5337
|
}
|
|
5323
5338
|
function appendRetentionEvent(directory, event) {
|
|
5324
5339
|
try {
|
|
@@ -5335,9 +5350,20 @@ function getCurrentSha(directory) {
|
|
|
5335
5350
|
function isGitRepo2(directory) {
|
|
5336
5351
|
try {
|
|
5337
5352
|
gitExec(["rev-parse", "--git-dir"], directory);
|
|
5338
|
-
return true;
|
|
5339
|
-
} catch {
|
|
5340
|
-
|
|
5353
|
+
return { isRepo: true };
|
|
5354
|
+
} catch (e) {
|
|
5355
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
5356
|
+
const isTransient = /ETIMEDOUT|timed out/i.test(message) && !/not a git repository/i.test(message);
|
|
5357
|
+
if (isTransient) {
|
|
5358
|
+
return {
|
|
5359
|
+
isRepo: false,
|
|
5360
|
+
warning: "git probe failed after retry exhaustion \u2014 treating as not a git repository"
|
|
5361
|
+
};
|
|
5362
|
+
}
|
|
5363
|
+
return {
|
|
5364
|
+
isRepo: false,
|
|
5365
|
+
warning: "git probe failed \u2014 directory may not be a git repository"
|
|
5366
|
+
};
|
|
5341
5367
|
}
|
|
5342
5368
|
}
|
|
5343
5369
|
function handleSave(label, directory) {
|
|
@@ -5480,11 +5506,12 @@ var checkpoint = createSwarmTool({
|
|
|
5480
5506
|
label: exports_external.string().optional().describe("Checkpoint label (required for save, restore, delete)")
|
|
5481
5507
|
},
|
|
5482
5508
|
execute: async (args, directory) => {
|
|
5483
|
-
|
|
5509
|
+
const repoProbe = isGitRepo2(directory);
|
|
5510
|
+
if (!repoProbe.isRepo) {
|
|
5484
5511
|
return JSON.stringify({
|
|
5485
5512
|
action: "unknown",
|
|
5486
5513
|
success: false,
|
|
5487
|
-
error: "not a git repository"
|
|
5514
|
+
error: `${repoProbe.warning ?? "not a git repository"} \u2014 checkpoint tools require a git repository`
|
|
5488
5515
|
}, null, 2);
|
|
5489
5516
|
}
|
|
5490
5517
|
let action;
|
|
@@ -5673,6 +5700,7 @@ async function handleClarifyCommand(_directory, args) {
|
|
|
5673
5700
|
}
|
|
5674
5701
|
|
|
5675
5702
|
// src/commands/close.ts
|
|
5703
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
5676
5704
|
import * as fsSync2 from "fs";
|
|
5677
5705
|
import { promises as fs11 } from "fs";
|
|
5678
5706
|
import path24 from "path";
|
|
@@ -6079,7 +6107,8 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6079
6107
|
} catch {
|
|
6080
6108
|
warnings.push("Failed to load plan data.");
|
|
6081
6109
|
}
|
|
6082
|
-
const
|
|
6110
|
+
const effectivePlanId = planId === "unknown" ? `unknown-${Date.now()}` : planId;
|
|
6111
|
+
const reportFilename = `post-mortem-${effectivePlanId}.md`;
|
|
6083
6112
|
let reportPath;
|
|
6084
6113
|
try {
|
|
6085
6114
|
reportPath = validateSwarmPath(directory, reportFilename);
|
|
@@ -6095,22 +6124,22 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6095
6124
|
if (!options.force && isReportValid(reportPath)) {
|
|
6096
6125
|
return {
|
|
6097
6126
|
success: true,
|
|
6098
|
-
planId,
|
|
6127
|
+
planId: effectivePlanId,
|
|
6099
6128
|
reportPath,
|
|
6100
6129
|
summary: "Post-mortem report already exists (idempotent skip).",
|
|
6101
6130
|
warnings
|
|
6102
6131
|
};
|
|
6103
6132
|
}
|
|
6104
|
-
const lock = await _internals13.acquirePostMortemLock(directory,
|
|
6133
|
+
const lock = await _internals13.acquirePostMortemLock(directory, effectivePlanId);
|
|
6105
6134
|
if (!lock.acquired) {
|
|
6106
6135
|
return {
|
|
6107
6136
|
success: false,
|
|
6108
|
-
planId,
|
|
6137
|
+
planId: effectivePlanId,
|
|
6109
6138
|
reportPath,
|
|
6110
6139
|
summary: null,
|
|
6111
6140
|
warnings: [
|
|
6112
6141
|
...warnings,
|
|
6113
|
-
`Concurrent post-mortem run in progress for plan ${
|
|
6142
|
+
`Concurrent post-mortem run in progress for plan ${effectivePlanId}; skipped.`
|
|
6114
6143
|
]
|
|
6115
6144
|
};
|
|
6116
6145
|
}
|
|
@@ -6160,7 +6189,7 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6160
6189
|
if (options.llmDelegate) {
|
|
6161
6190
|
try {
|
|
6162
6191
|
const { CURATOR_POSTMORTEM_PROMPT: CURATOR_POSTMORTEM_PROMPT2 } = await import("./explorer-gz70sm9b.js");
|
|
6163
|
-
const userInput = assembleLLMInput(
|
|
6192
|
+
const userInput = assembleLLMInput(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
6164
6193
|
const ac = new AbortController;
|
|
6165
6194
|
const timer = setTimeout(() => ac.abort(), 300000);
|
|
6166
6195
|
let llmOutput;
|
|
@@ -6176,17 +6205,17 @@ async function runCuratorPostMortem(directory, options = {}) {
|
|
|
6176
6205
|
} finally {
|
|
6177
6206
|
clearTimeout(timer);
|
|
6178
6207
|
}
|
|
6179
|
-
reportContent = `# Post-Mortem Report: ${
|
|
6208
|
+
reportContent = `# Post-Mortem Report: ${effectivePlanId}
|
|
6180
6209
|
Generated: ${new Date().toISOString()}
|
|
6181
6210
|
|
|
6182
6211
|
${llmOutput}`;
|
|
6183
6212
|
} catch (err) {
|
|
6184
6213
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6185
6214
|
warnings.push(`LLM delegate failed, falling back to data-only report: ${msg}`);
|
|
6186
|
-
reportContent = _internals13.buildDataOnlyReport(
|
|
6215
|
+
reportContent = _internals13.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
6187
6216
|
}
|
|
6188
6217
|
} else {
|
|
6189
|
-
reportContent = _internals13.buildDataOnlyReport(
|
|
6218
|
+
reportContent = _internals13.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
6190
6219
|
}
|
|
6191
6220
|
try {
|
|
6192
6221
|
const { mkdirSync: mkdirSync6 } = await import("fs");
|
|
@@ -6196,7 +6225,7 @@ ${llmOutput}`;
|
|
|
6196
6225
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6197
6226
|
return {
|
|
6198
6227
|
success: false,
|
|
6199
|
-
planId,
|
|
6228
|
+
planId: effectivePlanId,
|
|
6200
6229
|
reportPath: null,
|
|
6201
6230
|
summary: null,
|
|
6202
6231
|
warnings: [...warnings, `Failed to write report: ${msg}`]
|
|
@@ -6206,13 +6235,13 @@ ${llmOutput}`;
|
|
|
6206
6235
|
const neverAppliedCount = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0).length;
|
|
6207
6236
|
const totalViolations = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
|
|
6208
6237
|
const summary = [
|
|
6209
|
-
`Post-mortem for plan "${
|
|
6238
|
+
`Post-mortem for plan "${effectivePlanId}": ${totalEntries} knowledge entries reviewed.`,
|
|
6210
6239
|
`${neverAppliedCount} never-applied entries flagged; ${totalViolations} total violations recorded.`,
|
|
6211
6240
|
`${proposals.length} pending proposals, ${unactionable.length} quarantined entries.`
|
|
6212
6241
|
].join(" ");
|
|
6213
6242
|
return {
|
|
6214
6243
|
success: true,
|
|
6215
|
-
planId,
|
|
6244
|
+
planId: effectivePlanId,
|
|
6216
6245
|
reportPath,
|
|
6217
6246
|
summary,
|
|
6218
6247
|
warnings
|
|
@@ -11097,8 +11126,9 @@ function countSessionKnowledgeEntries(entries, sessionStart, fallbackCount) {
|
|
|
11097
11126
|
return Number.isFinite(createdAtMs) && createdAtMs >= sessionStartMs;
|
|
11098
11127
|
}).length;
|
|
11099
11128
|
}
|
|
11100
|
-
async function
|
|
11129
|
+
async function copyDirRecursiveWithFailures(src, dest) {
|
|
11101
11130
|
let count = 0;
|
|
11131
|
+
const failures = [];
|
|
11102
11132
|
const entries = await fs11.readdir(src);
|
|
11103
11133
|
await fs11.mkdir(dest, { recursive: true });
|
|
11104
11134
|
for (const entry of entries) {
|
|
@@ -11107,17 +11137,32 @@ async function copyDirRecursive(src, dest) {
|
|
|
11107
11137
|
try {
|
|
11108
11138
|
const stat4 = await fs11.stat(srcEntry);
|
|
11109
11139
|
if (stat4.isDirectory()) {
|
|
11110
|
-
const
|
|
11111
|
-
count +=
|
|
11140
|
+
const subResult = await copyDirRecursiveWithFailures(srcEntry, destEntry);
|
|
11141
|
+
count += subResult.copied;
|
|
11142
|
+
failures.push(...subResult.failures);
|
|
11112
11143
|
} else {
|
|
11113
11144
|
try {
|
|
11114
11145
|
await fs11.copyFile(srcEntry, destEntry);
|
|
11115
11146
|
count++;
|
|
11116
|
-
} catch {
|
|
11147
|
+
} catch (err) {
|
|
11148
|
+
const errno = err?.code;
|
|
11149
|
+
if (errno !== "ENOENT") {
|
|
11150
|
+
failures.push(`${srcEntry}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11151
|
+
}
|
|
11152
|
+
}
|
|
11117
11153
|
}
|
|
11118
|
-
} catch {
|
|
11154
|
+
} catch (err) {
|
|
11155
|
+
const errno = err?.code;
|
|
11156
|
+
if (errno !== "ENOENT") {
|
|
11157
|
+
failures.push(`${srcEntry}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11158
|
+
}
|
|
11159
|
+
}
|
|
11119
11160
|
}
|
|
11120
|
-
return count;
|
|
11161
|
+
return { copied: count, failures };
|
|
11162
|
+
}
|
|
11163
|
+
async function copyDirRecursive(src, dest) {
|
|
11164
|
+
const result = await copyDirRecursiveWithFailures(src, dest);
|
|
11165
|
+
return result.copied;
|
|
11121
11166
|
}
|
|
11122
11167
|
var ARCHIVE_ARTIFACTS = [
|
|
11123
11168
|
"plan.json",
|
|
@@ -11436,31 +11481,145 @@ async function runFinalizeStage(ctx) {
|
|
|
11436
11481
|
ctx.warnings.push(`Post-mortem failed: ${msg}`);
|
|
11437
11482
|
}
|
|
11438
11483
|
}
|
|
11484
|
+
async function copySqliteSafe(srcPath, destPath) {
|
|
11485
|
+
if (!fsSync2.existsSync(srcPath)) {
|
|
11486
|
+
return {
|
|
11487
|
+
success: true,
|
|
11488
|
+
skipped: true,
|
|
11489
|
+
reason: "source does not exist (ENOENT)"
|
|
11490
|
+
};
|
|
11491
|
+
}
|
|
11492
|
+
let checkpointVerified = false;
|
|
11493
|
+
try {
|
|
11494
|
+
const result = spawnSync5("sqlite3", [srcPath, "PRAGMA wal_checkpoint(TRUNCATE);"], {
|
|
11495
|
+
cwd: path24.dirname(srcPath),
|
|
11496
|
+
encoding: "utf-8",
|
|
11497
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11498
|
+
timeout: 1e4,
|
|
11499
|
+
windowsHide: true,
|
|
11500
|
+
maxBuffer: 1024
|
|
11501
|
+
});
|
|
11502
|
+
if (result.error) {
|
|
11503
|
+
const code = result.error.code;
|
|
11504
|
+
if (code === "ENOENT") {
|
|
11505
|
+
try {
|
|
11506
|
+
await fs11.copyFile(srcPath, destPath);
|
|
11507
|
+
return {
|
|
11508
|
+
success: true,
|
|
11509
|
+
reason: "copied without WAL checkpoint (sqlite3 CLI unavailable)"
|
|
11510
|
+
};
|
|
11511
|
+
} catch (copyErr) {
|
|
11512
|
+
return {
|
|
11513
|
+
success: false,
|
|
11514
|
+
reason: `fallback copy failed: ${copyErr instanceof Error ? copyErr.message : String(copyErr)}`
|
|
11515
|
+
};
|
|
11516
|
+
}
|
|
11517
|
+
}
|
|
11518
|
+
return {
|
|
11519
|
+
success: false,
|
|
11520
|
+
reason: `wal_checkpoint failed: ${result.error instanceof Error ? result.error.message : String(result.error)}`
|
|
11521
|
+
};
|
|
11522
|
+
}
|
|
11523
|
+
if (result.status !== 0) {
|
|
11524
|
+
return {
|
|
11525
|
+
success: false,
|
|
11526
|
+
reason: `wal_checkpoint exited with code ${result.status}`
|
|
11527
|
+
};
|
|
11528
|
+
}
|
|
11529
|
+
const output = (result.stdout || "").trim();
|
|
11530
|
+
const lines = output.split(`
|
|
11531
|
+
`).filter((l) => l.trim());
|
|
11532
|
+
if (lines.length >= 1) {
|
|
11533
|
+
const dataLine = lines[0];
|
|
11534
|
+
const columns = dataLine.split("|");
|
|
11535
|
+
const busyFlag = parseInt(columns[0], 10);
|
|
11536
|
+
checkpointVerified = !Number.isNaN(busyFlag) && busyFlag === 0;
|
|
11537
|
+
}
|
|
11538
|
+
} catch (err) {
|
|
11539
|
+
return {
|
|
11540
|
+
success: false,
|
|
11541
|
+
reason: `wal_checkpoint error: ${err instanceof Error ? err.message : String(err)}`
|
|
11542
|
+
};
|
|
11543
|
+
}
|
|
11544
|
+
try {
|
|
11545
|
+
await fs11.copyFile(srcPath, destPath);
|
|
11546
|
+
if (checkpointVerified) {
|
|
11547
|
+
return { success: true };
|
|
11548
|
+
}
|
|
11549
|
+
return {
|
|
11550
|
+
success: true,
|
|
11551
|
+
reason: "WAL checkpoint incomplete (busy) \u2014 archive copy may be stale, original preserved"
|
|
11552
|
+
};
|
|
11553
|
+
} catch (err) {
|
|
11554
|
+
return {
|
|
11555
|
+
success: false,
|
|
11556
|
+
reason: `copy failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11557
|
+
};
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11439
11560
|
async function runArchiveStage(ctx) {
|
|
11440
11561
|
ctx.timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
11441
11562
|
ctx.archiveSuffix = Math.random().toString(36).slice(2, 8);
|
|
11442
11563
|
ctx.archiveDir = path24.join(ctx.swarmDir, "archive", `swarm-${ctx.timestamp}-${ctx.archiveSuffix}`);
|
|
11443
11564
|
try {
|
|
11444
11565
|
await fs11.mkdir(ctx.archiveDir, { recursive: true });
|
|
11566
|
+
const WAL_SIDECAR_FILES = new Set(["swarm.db-shm", "swarm.db-wal"]);
|
|
11445
11567
|
for (const artifact of ARCHIVE_ARTIFACTS) {
|
|
11568
|
+
if (WAL_SIDECAR_FILES.has(artifact)) {
|
|
11569
|
+
continue;
|
|
11570
|
+
}
|
|
11446
11571
|
const srcPath = path24.join(ctx.swarmDir, artifact);
|
|
11447
11572
|
const destPath = path24.join(ctx.archiveDir, artifact);
|
|
11448
|
-
|
|
11449
|
-
await
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11573
|
+
if (artifact === "swarm.db") {
|
|
11574
|
+
const result = await copySqliteSafe(srcPath, destPath);
|
|
11575
|
+
if (result.skipped) {} else if (result.success) {
|
|
11576
|
+
ctx.archivedFileCount++;
|
|
11577
|
+
if (result.reason) {
|
|
11578
|
+
ctx.warnings.push(`Archived ${artifact}: ${result.reason}. Original preserved to prevent data loss.`);
|
|
11579
|
+
} else {
|
|
11580
|
+
ctx.archivedActiveStateFiles.add(artifact);
|
|
11581
|
+
}
|
|
11582
|
+
} else {
|
|
11583
|
+
ctx.archiveFailureReasons.set(artifact, result.reason);
|
|
11584
|
+
ctx.warnings.push(`Failed to archive ${artifact}: ${result.reason}. File preserved (not cleaned up).`);
|
|
11453
11585
|
}
|
|
11454
|
-
}
|
|
11586
|
+
} else {
|
|
11587
|
+
try {
|
|
11588
|
+
await fs11.copyFile(srcPath, destPath);
|
|
11589
|
+
ctx.archivedFileCount++;
|
|
11590
|
+
if (ACTIVE_STATE_TO_CLEAN.includes(artifact)) {
|
|
11591
|
+
ctx.archivedActiveStateFiles.add(artifact);
|
|
11592
|
+
}
|
|
11593
|
+
} catch (err) {
|
|
11594
|
+
const errno = err?.code;
|
|
11595
|
+
if (errno === "ENOENT") {} else {
|
|
11596
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11597
|
+
ctx.archiveFailureReasons.set(artifact, `${errno ?? "unknown"}: ${reason}`);
|
|
11598
|
+
ctx.warnings.push(`Failed to archive ${artifact} [${errno ?? "unknown"}]: ${reason}. File preserved (not cleaned up).`);
|
|
11599
|
+
}
|
|
11600
|
+
}
|
|
11601
|
+
}
|
|
11455
11602
|
}
|
|
11456
11603
|
for (const dirName of ACTIVE_STATE_DIRS_TO_CLEAN) {
|
|
11457
11604
|
const srcDir = path24.join(ctx.swarmDir, dirName);
|
|
11458
11605
|
const destDir = path24.join(ctx.archiveDir, dirName);
|
|
11459
11606
|
try {
|
|
11460
|
-
const
|
|
11461
|
-
ctx.archivedFileCount += copied;
|
|
11462
|
-
|
|
11463
|
-
|
|
11607
|
+
const result = await copyDirRecursiveWithFailures(srcDir, destDir);
|
|
11608
|
+
ctx.archivedFileCount += result.copied;
|
|
11609
|
+
if (result.failures.length === 0) {
|
|
11610
|
+
ctx.archivedActiveStateDirs.add(dirName);
|
|
11611
|
+
} else {
|
|
11612
|
+
ctx.warnings.push(`Directory ${dirName} not fully archived (${result.failures.length} failure(s)). Source preserved.`);
|
|
11613
|
+
for (const failure of result.failures) {
|
|
11614
|
+
ctx.warnings.push(` - ${failure}`);
|
|
11615
|
+
}
|
|
11616
|
+
}
|
|
11617
|
+
} catch (err) {
|
|
11618
|
+
const code = err.code;
|
|
11619
|
+
if (code !== "ENOENT") {
|
|
11620
|
+
ctx.warnings.push(`Failed to archive directory ${dirName} [${code ?? "unknown"}]: ${err.message}. Source preserved.`);
|
|
11621
|
+
}
|
|
11622
|
+
}
|
|
11464
11623
|
}
|
|
11465
11624
|
ctx.archiveResult = `Archived ${ctx.archivedFileCount} artifact(s) to .swarm/archive/swarm-${ctx.timestamp}-${ctx.archiveSuffix}/`;
|
|
11466
11625
|
} catch (archiveError) {
|
|
@@ -11501,14 +11660,21 @@ async function runCleanStage(ctx) {
|
|
|
11501
11660
|
if (ctx.archivedActiveStateFiles.size > 0) {
|
|
11502
11661
|
for (const artifact of ACTIVE_STATE_TO_CLEAN) {
|
|
11503
11662
|
if (!ctx.archivedActiveStateFiles.has(artifact)) {
|
|
11504
|
-
ctx.
|
|
11663
|
+
const reason = ctx.archiveFailureReasons?.get(artifact);
|
|
11664
|
+
ctx.warnings.push(reason ? `Preserved ${artifact} because it was not successfully archived: ${reason}.` : `Preserved ${artifact} because it was not successfully archived.`);
|
|
11505
11665
|
continue;
|
|
11506
11666
|
}
|
|
11507
11667
|
const filePath = path24.join(ctx.swarmDir, artifact);
|
|
11508
11668
|
try {
|
|
11509
11669
|
await fs11.unlink(filePath);
|
|
11510
11670
|
cleanedFiles.push(artifact);
|
|
11511
|
-
} catch {
|
|
11671
|
+
} catch (err) {
|
|
11672
|
+
const errno = err?.code;
|
|
11673
|
+
if (errno === "ENOENT") {} else {
|
|
11674
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11675
|
+
ctx.warnings.push(`Failed to clean active-state file ${artifact} [${errno ?? "unknown"}]: ${reason}`);
|
|
11676
|
+
}
|
|
11677
|
+
}
|
|
11512
11678
|
}
|
|
11513
11679
|
} else {
|
|
11514
11680
|
ctx.warnings.push("Skipped active-state cleanup because no active-state files were archived. Files preserved to prevent data loss.");
|
|
@@ -11530,15 +11696,33 @@ async function runCleanStage(ctx) {
|
|
|
11530
11696
|
try {
|
|
11531
11697
|
await fs11.unlink(path24.join(ctx.swarmDir, backup));
|
|
11532
11698
|
configBackupsRemoved++;
|
|
11533
|
-
} catch {
|
|
11699
|
+
} catch (err) {
|
|
11700
|
+
const errno = err?.code;
|
|
11701
|
+
if (errno === "ENOENT") {} else {
|
|
11702
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11703
|
+
ctx.warnings.push(`Failed to clean config-backup ${backup} [${errno ?? "unknown"}]: ${reason}`);
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11534
11706
|
}
|
|
11535
11707
|
const ledgerSiblings = swarmFiles.filter((f) => (f.startsWith("plan-ledger.archived-") || f.startsWith("plan-ledger.backup-")) && f.endsWith(".jsonl"));
|
|
11536
11708
|
for (const sibling of ledgerSiblings) {
|
|
11537
11709
|
try {
|
|
11538
11710
|
await fs11.unlink(path24.join(ctx.swarmDir, sibling));
|
|
11539
|
-
} catch {
|
|
11711
|
+
} catch (err) {
|
|
11712
|
+
const errno = err?.code;
|
|
11713
|
+
if (errno === "ENOENT") {} else {
|
|
11714
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11715
|
+
ctx.warnings.push(`Failed to clean ledger sibling ${sibling} [${errno ?? "unknown"}]: ${reason}`);
|
|
11716
|
+
}
|
|
11717
|
+
}
|
|
11540
11718
|
}
|
|
11541
|
-
} catch {
|
|
11719
|
+
} catch (err) {
|
|
11720
|
+
const errno = err?.code;
|
|
11721
|
+
if (errno === "ENOENT") {} else {
|
|
11722
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11723
|
+
ctx.warnings.push(`Failed to read ${ctx.swarmDir} for stale-file cleanup [${errno ?? "unknown"}]: ${reason}`);
|
|
11724
|
+
}
|
|
11725
|
+
}
|
|
11542
11726
|
let swarmPlanFilesRemoved = 0;
|
|
11543
11727
|
const candidates = [
|
|
11544
11728
|
path24.join(ctx.directory, ".swarm", "SWARM_PLAN.json"),
|
|
@@ -11564,9 +11748,21 @@ async function runCleanStage(ctx) {
|
|
|
11564
11748
|
try {
|
|
11565
11749
|
await fs11.unlink(path24.join(ctx.swarmDir, tmp));
|
|
11566
11750
|
tmpFilesRemoved++;
|
|
11567
|
-
} catch {
|
|
11751
|
+
} catch (err) {
|
|
11752
|
+
const errno = err?.code;
|
|
11753
|
+
if (errno === "ENOENT") {} else {
|
|
11754
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11755
|
+
ctx.warnings.push(`Failed to clean tmp file ${tmp} [${errno ?? "unknown"}]: ${reason}`);
|
|
11756
|
+
}
|
|
11757
|
+
}
|
|
11568
11758
|
}
|
|
11569
|
-
} catch {
|
|
11759
|
+
} catch (err) {
|
|
11760
|
+
const errno = err?.code;
|
|
11761
|
+
if (errno === "ENOENT") {} else {
|
|
11762
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
11763
|
+
ctx.warnings.push(`Failed to read ${ctx.swarmDir} for tmp-file cleanup [${errno ?? "unknown"}]: ${reason}`);
|
|
11764
|
+
}
|
|
11765
|
+
}
|
|
11570
11766
|
if (tmpFilesRemoved > 0) {
|
|
11571
11767
|
cleanedFiles.push(`${tmpFilesRemoved} .tmp.* file(s)`);
|
|
11572
11768
|
}
|
|
@@ -11583,9 +11779,14 @@ async function runCleanStage(ctx) {
|
|
|
11583
11779
|
""
|
|
11584
11780
|
].join(`
|
|
11585
11781
|
`);
|
|
11782
|
+
const contextTempPath = path24.join(path24.dirname(contextPath), `${path24.basename(contextPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
11586
11783
|
try {
|
|
11587
|
-
await fs11.writeFile(
|
|
11784
|
+
await fs11.writeFile(contextTempPath, contextContent, "utf-8");
|
|
11785
|
+
fsSync2.renameSync(contextTempPath, contextPath);
|
|
11588
11786
|
} catch (error2) {
|
|
11787
|
+
try {
|
|
11788
|
+
fsSync2.unlinkSync(contextTempPath);
|
|
11789
|
+
} catch {}
|
|
11589
11790
|
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
11590
11791
|
ctx.warnings.push(`Failed to reset context.md: ${msg}`);
|
|
11591
11792
|
console.warn("[close-command] Failed to write context.md:", error2);
|
|
@@ -11679,15 +11880,33 @@ async function handleCloseCommand(directory, args, options = {}) {
|
|
|
11679
11880
|
if (!finalizeLock.acquired) {
|
|
11680
11881
|
return `\u274C Another /swarm finalize is already running for this project. If you are certain no other run is active, wait for the lock to expire or remove the stale lock and retry.`;
|
|
11681
11882
|
}
|
|
11682
|
-
const phases = planData.phases ?? [];
|
|
11683
|
-
const inProgressPhases = phases.filter((p) => p.status === "in_progress");
|
|
11684
|
-
const isForced = args.includes("--force");
|
|
11685
|
-
const runSkillReview = args.includes("--skill-review");
|
|
11686
|
-
let planAlreadyDone = false;
|
|
11687
|
-
if (planExists) {
|
|
11688
|
-
planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
|
|
11689
|
-
}
|
|
11690
11883
|
try {
|
|
11884
|
+
if (!planExists) {
|
|
11885
|
+
const archiveDir = path24.join(swarmDir, "archive");
|
|
11886
|
+
try {
|
|
11887
|
+
const archiveEntries = await fs11.readdir(archiveDir);
|
|
11888
|
+
const hasArchiveBundle = archiveEntries.some((entry) => entry.startsWith("swarm-"));
|
|
11889
|
+
if (hasArchiveBundle) {
|
|
11890
|
+
const hasActiveState = [
|
|
11891
|
+
...ACTIVE_STATE_TO_CLEAN,
|
|
11892
|
+
...ACTIVE_STATE_DIRS_TO_CLEAN
|
|
11893
|
+
].some((entry) => fsSync2.existsSync(path24.join(swarmDir, entry)));
|
|
11894
|
+
if (!hasActiveState) {
|
|
11895
|
+
return `\u2705 Already finalized \u2014 nothing to do.
|
|
11896
|
+
|
|
11897
|
+
This project was already finalized in a previous /swarm close run. The plan has been archived and cleaned up. No further action is needed.`;
|
|
11898
|
+
}
|
|
11899
|
+
}
|
|
11900
|
+
} catch {}
|
|
11901
|
+
}
|
|
11902
|
+
const phases = planData.phases ?? [];
|
|
11903
|
+
const inProgressPhases = phases.filter((p) => p.status === "in_progress");
|
|
11904
|
+
const isForced = args.includes("--force");
|
|
11905
|
+
const runSkillReview = args.includes("--skill-review");
|
|
11906
|
+
let planAlreadyDone = false;
|
|
11907
|
+
if (planExists) {
|
|
11908
|
+
planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
|
|
11909
|
+
}
|
|
11691
11910
|
const { config: loadedConfig } = _internals19.loadPluginConfigWithMeta(directory);
|
|
11692
11911
|
const config = KnowledgeConfigSchema.parse(loadedConfig.knowledge ?? {});
|
|
11693
11912
|
const ctx = {
|
|
@@ -11724,6 +11943,7 @@ async function handleCloseCommand(directory, args, options = {}) {
|
|
|
11724
11943
|
archivedFileCount: 0,
|
|
11725
11944
|
archivedActiveStateFiles: new Set,
|
|
11726
11945
|
archivedActiveStateDirs: new Set,
|
|
11946
|
+
archiveFailureReasons: new Map,
|
|
11727
11947
|
timestamp: "",
|
|
11728
11948
|
archiveDir: "",
|
|
11729
11949
|
archiveSuffix: "",
|
|
@@ -11786,9 +12006,14 @@ async function handleCloseCommand(directory, args, options = {}) {
|
|
|
11786
12006
|
...ctx.warnings.length > 0 ? ["## Warnings", ...ctx.warnings.map((w) => `- ${w}`), ""] : []
|
|
11787
12007
|
].join(`
|
|
11788
12008
|
`);
|
|
12009
|
+
const closeSummaryTempPath = path24.join(path24.dirname(closeSummaryPath), `${path24.basename(closeSummaryPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
11789
12010
|
try {
|
|
11790
|
-
await fs11.writeFile(
|
|
12011
|
+
await fs11.writeFile(closeSummaryTempPath, summaryContent, "utf-8");
|
|
12012
|
+
fsSync2.renameSync(closeSummaryTempPath, closeSummaryPath);
|
|
11791
12013
|
} catch (error2) {
|
|
12014
|
+
try {
|
|
12015
|
+
fsSync2.unlinkSync(closeSummaryTempPath);
|
|
12016
|
+
} catch {}
|
|
11792
12017
|
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
11793
12018
|
ctx.warnings.push(`Failed to write close-summary.md: ${msg}`);
|
|
11794
12019
|
console.warn("[close-command] Failed to write close-summary.md:", error2);
|
|
@@ -12129,7 +12354,7 @@ async function handleConfigCommand(directory, _args) {
|
|
|
12129
12354
|
}
|
|
12130
12355
|
|
|
12131
12356
|
// src/services/skill-consolidation.ts
|
|
12132
|
-
import { existsSync as
|
|
12357
|
+
import { existsSync as existsSync14 } from "fs";
|
|
12133
12358
|
import { mkdir as mkdir8, readFile as readFile8, rename as rename4, writeFile as writeFile8 } from "fs/promises";
|
|
12134
12359
|
import * as path26 from "path";
|
|
12135
12360
|
|
|
@@ -12160,7 +12385,7 @@ function consolidationStatePath(directory) {
|
|
|
12160
12385
|
}
|
|
12161
12386
|
async function readState2(directory) {
|
|
12162
12387
|
const filePath = consolidationStatePath(directory);
|
|
12163
|
-
if (!
|
|
12388
|
+
if (!existsSync14(filePath))
|
|
12164
12389
|
return {};
|
|
12165
12390
|
try {
|
|
12166
12391
|
const parsed = JSON.parse(await readFile8(filePath, "utf-8"));
|
|
@@ -13607,7 +13832,7 @@ ${USAGE5}`;
|
|
|
13607
13832
|
|
|
13608
13833
|
// src/services/diagnose-service.ts
|
|
13609
13834
|
import * as child_process4 from "child_process";
|
|
13610
|
-
import { existsSync as
|
|
13835
|
+
import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
|
|
13611
13836
|
import path31 from "path";
|
|
13612
13837
|
import { fileURLToPath } from "url";
|
|
13613
13838
|
|
|
@@ -13660,11 +13885,11 @@ init_capability_probe();
|
|
|
13660
13885
|
init_executor();
|
|
13661
13886
|
|
|
13662
13887
|
// src/services/knowledge-diagnostics.ts
|
|
13663
|
-
import { existsSync as
|
|
13888
|
+
import { existsSync as existsSync16 } from "fs";
|
|
13664
13889
|
import { readFile as readFile10 } from "fs/promises";
|
|
13665
13890
|
|
|
13666
13891
|
// src/services/version-check.ts
|
|
13667
|
-
import { existsSync as
|
|
13892
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
13668
13893
|
import { homedir as homedir4 } from "os";
|
|
13669
13894
|
import { join as join25 } from "path";
|
|
13670
13895
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -13679,7 +13904,7 @@ function cacheFile() {
|
|
|
13679
13904
|
function readVersionCache() {
|
|
13680
13905
|
try {
|
|
13681
13906
|
const path31 = cacheFile();
|
|
13682
|
-
if (!
|
|
13907
|
+
if (!existsSync15(path31))
|
|
13683
13908
|
return null;
|
|
13684
13909
|
const raw = readFileSync8(path31, "utf-8");
|
|
13685
13910
|
const parsed = JSON.parse(raw);
|
|
@@ -13720,7 +13945,7 @@ var SEVEN_DAYS_MS2 = 7 * 24 * 60 * 60 * 1000;
|
|
|
13720
13945
|
var UNACTIONABLE_BACKLOG_WARN = 100;
|
|
13721
13946
|
var INSIGHT_BACKLOG_WARN = 50;
|
|
13722
13947
|
async function readRawLines(filePath) {
|
|
13723
|
-
if (!
|
|
13948
|
+
if (!existsSync16(filePath))
|
|
13724
13949
|
return { entries: [], corrupt: 0 };
|
|
13725
13950
|
const content = await readFile10(filePath, "utf-8");
|
|
13726
13951
|
const entries = [];
|
|
@@ -13845,7 +14070,7 @@ async function computeKnowledgeDebug(directory) {
|
|
|
13845
14070
|
};
|
|
13846
14071
|
}
|
|
13847
14072
|
async function safeJsonlCount(filePath) {
|
|
13848
|
-
if (!filePath || !
|
|
14073
|
+
if (!filePath || !existsSync16(filePath))
|
|
13849
14074
|
return 0;
|
|
13850
14075
|
try {
|
|
13851
14076
|
const content = await readFile10(filePath, "utf-8");
|
|
@@ -14166,7 +14391,7 @@ async function checkConfigBackups(directory) {
|
|
|
14166
14391
|
}
|
|
14167
14392
|
async function checkGitRepository(directory) {
|
|
14168
14393
|
try {
|
|
14169
|
-
if (!
|
|
14394
|
+
if (!existsSync17(directory) || !statSync6(directory).isDirectory()) {
|
|
14170
14395
|
return {
|
|
14171
14396
|
name: "Git Repository",
|
|
14172
14397
|
status: "\u274C",
|
|
@@ -14231,7 +14456,7 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
14231
14456
|
}
|
|
14232
14457
|
async function checkConfigParseability(directory) {
|
|
14233
14458
|
const configPath = path31.join(directory, ".opencode/opencode-swarm.json");
|
|
14234
|
-
if (!
|
|
14459
|
+
if (!existsSync17(configPath)) {
|
|
14235
14460
|
return {
|
|
14236
14461
|
name: "Config Parseability",
|
|
14237
14462
|
status: "\u2705",
|
|
@@ -14286,11 +14511,11 @@ async function checkGrammarWasmFiles() {
|
|
|
14286
14511
|
const thisDir = path31.dirname(fileURLToPath(import.meta.url));
|
|
14287
14512
|
const grammarDir = resolveGrammarDir(thisDir);
|
|
14288
14513
|
const missing = [];
|
|
14289
|
-
if (!
|
|
14514
|
+
if (!existsSync17(path31.join(grammarDir, "tree-sitter.wasm"))) {
|
|
14290
14515
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
14291
14516
|
}
|
|
14292
14517
|
for (const file of grammarFiles) {
|
|
14293
|
-
if (!
|
|
14518
|
+
if (!existsSync17(path31.join(grammarDir, file))) {
|
|
14294
14519
|
missing.push(file);
|
|
14295
14520
|
}
|
|
14296
14521
|
}
|
|
@@ -14309,7 +14534,7 @@ async function checkGrammarWasmFiles() {
|
|
|
14309
14534
|
}
|
|
14310
14535
|
async function checkCheckpointManifest(directory) {
|
|
14311
14536
|
const manifestPath = path31.join(directory, ".swarm/checkpoints.json");
|
|
14312
|
-
if (!
|
|
14537
|
+
if (!existsSync17(manifestPath)) {
|
|
14313
14538
|
return {
|
|
14314
14539
|
name: "Checkpoint Manifest",
|
|
14315
14540
|
status: "\u2705",
|
|
@@ -14361,7 +14586,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
14361
14586
|
}
|
|
14362
14587
|
async function checkEventStreamIntegrity(directory) {
|
|
14363
14588
|
const eventsPath = path31.join(directory, ".swarm/events.jsonl");
|
|
14364
|
-
if (!
|
|
14589
|
+
if (!existsSync17(eventsPath)) {
|
|
14365
14590
|
return {
|
|
14366
14591
|
name: "Event Stream",
|
|
14367
14592
|
status: "\u2705",
|
|
@@ -14402,7 +14627,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
14402
14627
|
}
|
|
14403
14628
|
async function checkSteeringDirectives(directory) {
|
|
14404
14629
|
const eventsPath = path31.join(directory, ".swarm/events.jsonl");
|
|
14405
|
-
if (!
|
|
14630
|
+
if (!existsSync17(eventsPath)) {
|
|
14406
14631
|
return {
|
|
14407
14632
|
name: "Steering Directives",
|
|
14408
14633
|
status: "\u2705",
|
|
@@ -14458,7 +14683,7 @@ async function checkCurator(directory) {
|
|
|
14458
14683
|
};
|
|
14459
14684
|
}
|
|
14460
14685
|
const summaryPath = path31.join(directory, ".swarm/curator-summary.json");
|
|
14461
|
-
if (!
|
|
14686
|
+
if (!existsSync17(summaryPath)) {
|
|
14462
14687
|
return {
|
|
14463
14688
|
name: "Curator",
|
|
14464
14689
|
status: "\u2705",
|
|
@@ -14660,7 +14885,7 @@ async function getDiagnoseData(directory) {
|
|
|
14660
14885
|
checks.push(await checkKnowledgeHealth(directory));
|
|
14661
14886
|
try {
|
|
14662
14887
|
const evidenceDir = path31.join(directory, ".swarm", "evidence");
|
|
14663
|
-
const snapshotFiles =
|
|
14888
|
+
const snapshotFiles = existsSync17(evidenceDir) ? readdirSync3(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
|
|
14664
14889
|
if (snapshotFiles.length > 0) {
|
|
14665
14890
|
const latest = snapshotFiles.sort().pop();
|
|
14666
14891
|
checks.push({
|
|
@@ -14693,7 +14918,7 @@ async function getDiagnoseData(directory) {
|
|
|
14693
14918
|
const cacheRows = [];
|
|
14694
14919
|
for (const cachePath of cachePaths) {
|
|
14695
14920
|
try {
|
|
14696
|
-
if (!
|
|
14921
|
+
if (!existsSync17(cachePath)) {
|
|
14697
14922
|
cacheRows.push(`\u2B1C ${cachePath} \u2014 absent`);
|
|
14698
14923
|
continue;
|
|
14699
14924
|
}
|
|
@@ -15731,7 +15956,7 @@ async function handleEvidenceCommand(directory, args) {
|
|
|
15731
15956
|
return formatTaskEvidenceMarkdown(evidenceData);
|
|
15732
15957
|
}
|
|
15733
15958
|
async function handleEvidenceSummaryCommand(directory) {
|
|
15734
|
-
const { buildEvidenceSummary } = await import("./evidence-summary-service-
|
|
15959
|
+
const { buildEvidenceSummary } = await import("./evidence-summary-service-5me91eq8.js");
|
|
15735
15960
|
const artifact = await buildEvidenceSummary(directory);
|
|
15736
15961
|
if (!artifact) {
|
|
15737
15962
|
return "No plan found. Run `/swarm plan` to check plan status.";
|
|
@@ -16235,7 +16460,7 @@ function buildStatusReport(directory, sessionID, sessionFlag) {
|
|
|
16235
16460
|
|
|
16236
16461
|
// src/commands/handoff.ts
|
|
16237
16462
|
import crypto4 from "crypto";
|
|
16238
|
-
import { renameSync as
|
|
16463
|
+
import { renameSync as renameSync9, unlinkSync as unlinkSync5 } from "fs";
|
|
16239
16464
|
|
|
16240
16465
|
// src/services/handoff-service.ts
|
|
16241
16466
|
var RTL_OVERRIDE_PATTERN = /[\u202e\u202d\u202c\u200f]/g;
|
|
@@ -16615,7 +16840,7 @@ var _internals26 = {
|
|
|
16615
16840
|
};
|
|
16616
16841
|
|
|
16617
16842
|
// src/session/snapshot-writer.ts
|
|
16618
|
-
import { closeSync as closeSync6, fsyncSync as fsyncSync2, mkdirSync as mkdirSync14, openSync as openSync6, renameSync as
|
|
16843
|
+
import { closeSync as closeSync6, fsyncSync as fsyncSync2, mkdirSync as mkdirSync14, openSync as openSync6, renameSync as renameSync8 } from "fs";
|
|
16619
16844
|
import * as path36 from "path";
|
|
16620
16845
|
var _writeInFlight = Promise.resolve();
|
|
16621
16846
|
function serializeAgentSession(s) {
|
|
@@ -16735,7 +16960,7 @@ async function writeSnapshot(directory, state) {
|
|
|
16735
16960
|
closeSync6(fd);
|
|
16736
16961
|
}
|
|
16737
16962
|
} catch {}
|
|
16738
|
-
|
|
16963
|
+
renameSync8(tempPath, resolvedPath);
|
|
16739
16964
|
} catch (error2) {
|
|
16740
16965
|
log("[snapshot-writer] write failed", {
|
|
16741
16966
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
@@ -16767,10 +16992,10 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
16767
16992
|
const tempPath = `${resolvedPath}.tmp.${crypto4.randomUUID()}`;
|
|
16768
16993
|
await bunWrite(tempPath, markdown);
|
|
16769
16994
|
try {
|
|
16770
|
-
|
|
16995
|
+
renameSync9(tempPath, resolvedPath);
|
|
16771
16996
|
} catch (renameErr) {
|
|
16772
16997
|
try {
|
|
16773
|
-
|
|
16998
|
+
unlinkSync5(tempPath);
|
|
16774
16999
|
} catch {}
|
|
16775
17000
|
throw renameErr;
|
|
16776
17001
|
}
|
|
@@ -16779,10 +17004,10 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
16779
17004
|
const promptTempPath = `${promptPath}.tmp.${crypto4.randomUUID()}`;
|
|
16780
17005
|
await bunWrite(promptTempPath, continuationPrompt);
|
|
16781
17006
|
try {
|
|
16782
|
-
|
|
17007
|
+
renameSync9(promptTempPath, promptPath);
|
|
16783
17008
|
} catch (renameErr) {
|
|
16784
17009
|
try {
|
|
16785
|
-
|
|
17010
|
+
unlinkSync5(promptTempPath);
|
|
16786
17011
|
} catch {}
|
|
16787
17012
|
throw renameErr;
|
|
16788
17013
|
}
|
|
@@ -16942,7 +17167,7 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
16942
17167
|
return formatHistoryMarkdown(historyData);
|
|
16943
17168
|
}
|
|
16944
17169
|
// src/commands/_shared/url-security.ts
|
|
16945
|
-
import { spawnSync as
|
|
17170
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
16946
17171
|
var MAX_URL_LEN = 2048;
|
|
16947
17172
|
var IPV4_PRIVATE = /^10\./;
|
|
16948
17173
|
var IPV4_LOOPBACK = /^127\./;
|
|
@@ -16952,7 +17177,7 @@ var IPV4_PRIVATE_192 = /^192\.168\./;
|
|
|
16952
17177
|
var IPV4_ZERO_NETWORK = /^0\./;
|
|
16953
17178
|
var IPV6_LINK_LOCAL = /^fe80:/i;
|
|
16954
17179
|
var IPV6_UNIQUE_LOCAL = /^f[cd][0-9a-f]{2}:/i;
|
|
16955
|
-
var _internals28 = { spawnSync:
|
|
17180
|
+
var _internals28 = { spawnSync: spawnSync6 };
|
|
16956
17181
|
function sanitizeUrl(raw) {
|
|
16957
17182
|
let urlStr = raw.trim();
|
|
16958
17183
|
urlStr = urlStr.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
@@ -17231,7 +17456,7 @@ import { join as join30 } from "path";
|
|
|
17231
17456
|
// src/hooks/knowledge-migrator.ts
|
|
17232
17457
|
init_logger();
|
|
17233
17458
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
17234
|
-
import { existsSync as
|
|
17459
|
+
import { existsSync as existsSync22, readFileSync as readFileSync14 } from "fs";
|
|
17235
17460
|
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
17236
17461
|
import * as os8 from "os";
|
|
17237
17462
|
import * as path37 from "path";
|
|
@@ -17267,7 +17492,7 @@ async function migrateContextToKnowledge(directory, config) {
|
|
|
17267
17492
|
const sentinelPath = path37.join(directory, ".swarm", ".knowledge-migrated");
|
|
17268
17493
|
const contextPath = path37.join(directory, ".swarm", "context.md");
|
|
17269
17494
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
17270
|
-
if (
|
|
17495
|
+
if (existsSync22(sentinelPath)) {
|
|
17271
17496
|
return {
|
|
17272
17497
|
migrated: false,
|
|
17273
17498
|
entriesMigrated: 0,
|
|
@@ -17276,7 +17501,7 @@ async function migrateContextToKnowledge(directory, config) {
|
|
|
17276
17501
|
skippedReason: "sentinel-exists"
|
|
17277
17502
|
};
|
|
17278
17503
|
}
|
|
17279
|
-
if (!
|
|
17504
|
+
if (!existsSync22(contextPath)) {
|
|
17280
17505
|
return {
|
|
17281
17506
|
migrated: false,
|
|
17282
17507
|
entriesMigrated: 0,
|
|
@@ -17368,7 +17593,7 @@ async function migrateHiveKnowledgeLegacy(config) {
|
|
|
17368
17593
|
const legacyHivePath = _internals29.resolveLegacyHiveKnowledgePath();
|
|
17369
17594
|
const canonicalHivePath = resolveHiveKnowledgePath();
|
|
17370
17595
|
const sentinelPath = path37.join(path37.dirname(canonicalHivePath), ".hive-knowledge-migrated");
|
|
17371
|
-
if (
|
|
17596
|
+
if (existsSync22(sentinelPath)) {
|
|
17372
17597
|
return {
|
|
17373
17598
|
migrated: false,
|
|
17374
17599
|
entriesMigrated: 0,
|
|
@@ -17377,7 +17602,7 @@ async function migrateHiveKnowledgeLegacy(config) {
|
|
|
17377
17602
|
skippedReason: "sentinel-exists"
|
|
17378
17603
|
};
|
|
17379
17604
|
}
|
|
17380
|
-
if (!
|
|
17605
|
+
if (!existsSync22(legacyHivePath)) {
|
|
17381
17606
|
return {
|
|
17382
17607
|
migrated: false,
|
|
17383
17608
|
entriesMigrated: 0,
|
|
@@ -17579,7 +17804,7 @@ function truncateLesson2(text) {
|
|
|
17579
17804
|
}
|
|
17580
17805
|
function inferProjectName(directory) {
|
|
17581
17806
|
const packageJsonPath = path37.join(directory, "package.json");
|
|
17582
|
-
if (
|
|
17807
|
+
if (existsSync22(packageJsonPath)) {
|
|
17583
17808
|
try {
|
|
17584
17809
|
const pkg = JSON.parse(readFileSync14(packageJsonPath, "utf-8"));
|
|
17585
17810
|
if (pkg.name && typeof pkg.name === "string") {
|
|
@@ -18032,7 +18257,7 @@ ${USAGE7}`;
|
|
|
18032
18257
|
}
|
|
18033
18258
|
|
|
18034
18259
|
// src/commands/memory.ts
|
|
18035
|
-
import { existsSync as
|
|
18260
|
+
import { existsSync as existsSync25 } from "fs";
|
|
18036
18261
|
import * as path42 from "path";
|
|
18037
18262
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18038
18263
|
|
|
@@ -18130,7 +18355,7 @@ import * as path41 from "path";
|
|
|
18130
18355
|
|
|
18131
18356
|
// src/memory/local-jsonl-provider.ts
|
|
18132
18357
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18133
|
-
import { existsSync as
|
|
18358
|
+
import { existsSync as existsSync23 } from "fs";
|
|
18134
18359
|
import {
|
|
18135
18360
|
appendFile as appendFile3,
|
|
18136
18361
|
mkdir as mkdir10,
|
|
@@ -18161,7 +18386,26 @@ var SECRET_PATTERNS = [
|
|
|
18161
18386
|
},
|
|
18162
18387
|
{
|
|
18163
18388
|
type: "env_secret",
|
|
18164
|
-
pattern: /\b(?:[A-Z0-9]+_)
|
|
18389
|
+
pattern: /\b(?:[A-Z][A-Z0-9]+_)+(?:KEY|TOKEN|SECRET|PASSWORD)\b\s*=\s*["']?[^\s"'`]{8,}["']?/gi
|
|
18390
|
+
},
|
|
18391
|
+
{ type: "gitlab_token", pattern: /\bgl(?:pat|ptt)-[A-Za-z0-9_-]{15,}\b/g },
|
|
18392
|
+
{ type: "slack_token", pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g },
|
|
18393
|
+
{
|
|
18394
|
+
type: "jwt_token",
|
|
18395
|
+
pattern: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g
|
|
18396
|
+
},
|
|
18397
|
+
{
|
|
18398
|
+
type: "aws_secret_access_key",
|
|
18399
|
+
pattern: /\b(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY)\s*[=:]\s*[A-Za-z0-9/+=]{40}\b/g
|
|
18400
|
+
},
|
|
18401
|
+
{
|
|
18402
|
+
type: "stripe_secret_key",
|
|
18403
|
+
pattern: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{24,}\b/g
|
|
18404
|
+
},
|
|
18405
|
+
{ type: "google_api_key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
18406
|
+
{
|
|
18407
|
+
type: "openssh_private_key_block",
|
|
18408
|
+
pattern: /-----BEGIN[ ]OPENSSH[ ]PRIVATE[ ]KEY-----[\s\S]*?-----END[ ]OPENSSH[ ]PRIVATE[ ]KEY-----/g
|
|
18165
18409
|
}
|
|
18166
18410
|
];
|
|
18167
18411
|
function findSecrets(text) {
|
|
@@ -18732,7 +18976,7 @@ function normalizeMemoryAgentRole(agentRole) {
|
|
|
18732
18976
|
if (base === "critic" || base === "critic_sounding_board" || base === "critic_drift_verifier" || base === "critic_hallucination_verifier" || base === "critic_architecture_supervisor") {
|
|
18733
18977
|
return "security";
|
|
18734
18978
|
}
|
|
18735
|
-
if (base === "curator_init" || base === "curator_phase")
|
|
18979
|
+
if (base === "curator_init" || base === "curator_phase" || base === "curator_postmortem")
|
|
18736
18980
|
return "curator";
|
|
18737
18981
|
if (base === "docs")
|
|
18738
18982
|
return "sme";
|
|
@@ -18743,6 +18987,17 @@ function normalizeMemoryAgentRole(agentRole) {
|
|
|
18743
18987
|
}
|
|
18744
18988
|
|
|
18745
18989
|
// src/memory/scoring.ts
|
|
18990
|
+
var SCORING_WEIGHTS = {
|
|
18991
|
+
textOverlap: 0.38,
|
|
18992
|
+
tagOverlap: 0.16,
|
|
18993
|
+
fileOverlap: 0.12,
|
|
18994
|
+
symbolOverlap: 0.08,
|
|
18995
|
+
taskTermOverlap: 0.08,
|
|
18996
|
+
scopeSpecificityBoost: 0.12,
|
|
18997
|
+
kindProfileBoost: 0.06,
|
|
18998
|
+
roleBoost: 0.05,
|
|
18999
|
+
confidence: 0.08
|
|
19000
|
+
};
|
|
18746
19001
|
function tokenize(text) {
|
|
18747
19002
|
return new Set(text.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).map((token) => token.trim()).filter(Boolean));
|
|
18748
19003
|
}
|
|
@@ -18848,7 +19103,7 @@ function scoreMemoryRecordDetailed(record, request, context) {
|
|
|
18848
19103
|
if (request.mode === "injection" && request.requireQuerySignal !== false && !hasQuerySignal) {
|
|
18849
19104
|
return { item: null, skipReason: "no_signal" };
|
|
18850
19105
|
}
|
|
18851
|
-
const score = textOverlap *
|
|
19106
|
+
const score = textOverlap * SCORING_WEIGHTS.textOverlap + tagOverlap * SCORING_WEIGHTS.tagOverlap + fileOverlap * SCORING_WEIGHTS.fileOverlap + symbolOverlap * SCORING_WEIGHTS.symbolOverlap + taskTermOverlap * SCORING_WEIGHTS.taskTermOverlap + scopeSpecificityBoost(record.scope) * SCORING_WEIGHTS.scopeSpecificityBoost + kindProfileBoost(record.kind, request) * SCORING_WEIGHTS.kindProfileBoost + roleBoost * SCORING_WEIGHTS.roleBoost + record.confidence * SCORING_WEIGHTS.confidence;
|
|
18852
19107
|
const reasonParts = [
|
|
18853
19108
|
textOverlap > 0 ? `text_overlap=${textOverlap.toFixed(2)}` : null,
|
|
18854
19109
|
tagOverlap > 0 ? `tag_overlap=${tagOverlap.toFixed(2)}` : null,
|
|
@@ -19267,7 +19522,7 @@ function validateLoadedProposals(values, config) {
|
|
|
19267
19522
|
return { records, invalidCount };
|
|
19268
19523
|
}
|
|
19269
19524
|
async function readJsonl(filePath) {
|
|
19270
|
-
if (!
|
|
19525
|
+
if (!existsSync23(filePath))
|
|
19271
19526
|
return [];
|
|
19272
19527
|
const content = await readFile12(filePath, "utf-8");
|
|
19273
19528
|
const records = [];
|
|
@@ -19344,7 +19599,7 @@ import { createRequire as createRequire2 } from "module";
|
|
|
19344
19599
|
import * as path40 from "path";
|
|
19345
19600
|
|
|
19346
19601
|
// src/memory/jsonl-migration.ts
|
|
19347
|
-
import { existsSync as
|
|
19602
|
+
import { existsSync as existsSync24, renameSync as renameSync10, unlinkSync as unlinkSync6 } from "fs";
|
|
19348
19603
|
import { copyFile as copyFile2, mkdir as mkdir11, readFile as readFile13, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
19349
19604
|
import * as path39 from "path";
|
|
19350
19605
|
var LEGACY_JSONL_MIGRATION_VERSION = 2;
|
|
@@ -19378,10 +19633,10 @@ async function backupLegacyJsonl(rootDirectory, config = {}) {
|
|
|
19378
19633
|
const results = [];
|
|
19379
19634
|
for (const filename of ["memories.jsonl", "proposals.jsonl"]) {
|
|
19380
19635
|
const source = path39.join(storageDir, filename);
|
|
19381
|
-
if (!
|
|
19636
|
+
if (!existsSync24(source))
|
|
19382
19637
|
continue;
|
|
19383
19638
|
const backup = path39.join(backupDir, `${filename}.pre-sqlite-migration`);
|
|
19384
|
-
if (
|
|
19639
|
+
if (existsSync24(backup)) {
|
|
19385
19640
|
results.push({ source, backup, created: false });
|
|
19386
19641
|
continue;
|
|
19387
19642
|
}
|
|
@@ -19395,20 +19650,47 @@ async function writeJsonlExport(rootDirectory, config, memories, proposals) {
|
|
|
19395
19650
|
await mkdir11(exportDir, { recursive: true });
|
|
19396
19651
|
const memoriesPath = path39.join(exportDir, "memories.jsonl");
|
|
19397
19652
|
const proposalsPath = path39.join(exportDir, "proposals.jsonl");
|
|
19398
|
-
|
|
19399
|
-
|
|
19653
|
+
const memoriesTempPath = path39.join(path39.dirname(memoriesPath), `${path39.basename(memoriesPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19654
|
+
try {
|
|
19655
|
+
await writeFile11(memoriesTempPath, toJsonl(memories), "utf-8");
|
|
19656
|
+
renameSync10(memoriesTempPath, memoriesPath);
|
|
19657
|
+
} catch (err) {
|
|
19658
|
+
try {
|
|
19659
|
+
unlinkSync6(memoriesTempPath);
|
|
19660
|
+
} catch {}
|
|
19661
|
+
throw err;
|
|
19662
|
+
}
|
|
19663
|
+
const proposalsTempPath = path39.join(path39.dirname(proposalsPath), `${path39.basename(proposalsPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19664
|
+
try {
|
|
19665
|
+
await writeFile11(proposalsTempPath, toJsonl(proposals), "utf-8");
|
|
19666
|
+
renameSync10(proposalsTempPath, proposalsPath);
|
|
19667
|
+
} catch (err) {
|
|
19668
|
+
try {
|
|
19669
|
+
unlinkSync6(proposalsTempPath);
|
|
19670
|
+
} catch {}
|
|
19671
|
+
throw err;
|
|
19672
|
+
}
|
|
19400
19673
|
return { directory: exportDir, memoriesPath, proposalsPath };
|
|
19401
19674
|
}
|
|
19402
19675
|
async function writeMigrationReport(rootDirectory, report, config = {}) {
|
|
19403
19676
|
const reportPath = path39.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
19404
19677
|
await mkdir11(path39.dirname(reportPath), { recursive: true });
|
|
19405
|
-
|
|
19678
|
+
const reportTempPath = path39.join(path39.dirname(reportPath), `${path39.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
19679
|
+
try {
|
|
19680
|
+
await writeFile11(reportTempPath, `${JSON.stringify(report, null, 2)}
|
|
19406
19681
|
`, "utf-8");
|
|
19682
|
+
renameSync10(reportTempPath, reportPath);
|
|
19683
|
+
} catch (err) {
|
|
19684
|
+
try {
|
|
19685
|
+
unlinkSync6(reportTempPath);
|
|
19686
|
+
} catch {}
|
|
19687
|
+
throw err;
|
|
19688
|
+
}
|
|
19407
19689
|
return reportPath;
|
|
19408
19690
|
}
|
|
19409
19691
|
async function readMigrationReport(rootDirectory, config = {}) {
|
|
19410
19692
|
const reportPath = path39.join(resolveMemoryStorageDir(rootDirectory, config), "migration-report.json");
|
|
19411
|
-
if (!
|
|
19693
|
+
if (!existsSync24(reportPath))
|
|
19412
19694
|
return null;
|
|
19413
19695
|
try {
|
|
19414
19696
|
return JSON.parse(await readFile13(reportPath, "utf-8"));
|
|
@@ -19422,13 +19704,13 @@ async function getLegacyJsonlFileStatus(rootDirectory, config = {}) {
|
|
|
19422
19704
|
for (const file of ["memories.jsonl", "proposals.jsonl"]) {
|
|
19423
19705
|
const filePath = path39.join(storageDir, file);
|
|
19424
19706
|
let sizeBytes = 0;
|
|
19425
|
-
if (
|
|
19707
|
+
if (existsSync24(filePath)) {
|
|
19426
19708
|
sizeBytes = (await stat5(filePath)).size;
|
|
19427
19709
|
}
|
|
19428
19710
|
statuses.push({
|
|
19429
19711
|
file,
|
|
19430
19712
|
path: filePath,
|
|
19431
|
-
exists:
|
|
19713
|
+
exists: existsSync24(filePath),
|
|
19432
19714
|
sizeBytes
|
|
19433
19715
|
});
|
|
19434
19716
|
}
|
|
@@ -19509,7 +19791,7 @@ async function readProposalJsonl(filePath, config) {
|
|
|
19509
19791
|
return { records, invalidRows, totalRows: rows.totalRows };
|
|
19510
19792
|
}
|
|
19511
19793
|
async function readJsonlRows(filePath) {
|
|
19512
|
-
if (!
|
|
19794
|
+
if (!existsSync24(filePath)) {
|
|
19513
19795
|
return { rows: [], invalidRows: [], totalRows: 0 };
|
|
19514
19796
|
}
|
|
19515
19797
|
const content = await readFile13(filePath, "utf-8");
|
|
@@ -19548,7 +19830,6 @@ function loadDatabaseCtor2() {
|
|
|
19548
19830
|
_DatabaseCtor2 = req("bun:sqlite").Database;
|
|
19549
19831
|
return _DatabaseCtor2;
|
|
19550
19832
|
}
|
|
19551
|
-
var FTS_SCHEMA_MIGRATION_VERSION = 3;
|
|
19552
19833
|
var FTS_SCHEMA_MIGRATION_NAME = "create_memory_fts5_shadow_index";
|
|
19553
19834
|
var FTS_TABLE_NAME = "memory_items_fts";
|
|
19554
19835
|
var FTS_INDEX_COLUMNS = [
|
|
@@ -19637,6 +19918,15 @@ var MIGRATIONS2 = [
|
|
|
19637
19918
|
CREATE INDEX IF NOT EXISTS idx_memory_recall_usage_bundle
|
|
19638
19919
|
ON memory_recall_usage(bundle_id);
|
|
19639
19920
|
`
|
|
19921
|
+
},
|
|
19922
|
+
{
|
|
19923
|
+
version: 3,
|
|
19924
|
+
name: "create_memory_fts5_shadow_index",
|
|
19925
|
+
sql: `
|
|
19926
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS ${FTS_TABLE_NAME} USING fts5(
|
|
19927
|
+
${ftsCreateColumnsSql()}
|
|
19928
|
+
);
|
|
19929
|
+
`
|
|
19640
19930
|
}
|
|
19641
19931
|
];
|
|
19642
19932
|
|
|
@@ -19645,6 +19935,7 @@ class SQLiteMemoryProvider {
|
|
|
19645
19935
|
rootDirectory;
|
|
19646
19936
|
config;
|
|
19647
19937
|
initialized = false;
|
|
19938
|
+
initPromise = null;
|
|
19648
19939
|
db = null;
|
|
19649
19940
|
ftsAvailable = false;
|
|
19650
19941
|
memories = new Map;
|
|
@@ -19688,6 +19979,15 @@ class SQLiteMemoryProvider {
|
|
|
19688
19979
|
async initialize() {
|
|
19689
19980
|
if (this.initialized)
|
|
19690
19981
|
return;
|
|
19982
|
+
if (!this.initPromise) {
|
|
19983
|
+
this.initPromise = this.doInitialize().catch((err) => {
|
|
19984
|
+
this.initPromise = null;
|
|
19985
|
+
throw err;
|
|
19986
|
+
});
|
|
19987
|
+
}
|
|
19988
|
+
return this.initPromise;
|
|
19989
|
+
}
|
|
19990
|
+
async doInitialize() {
|
|
19691
19991
|
const dbPath = this.databasePath();
|
|
19692
19992
|
mkdirSync15(path40.dirname(dbPath), { recursive: true });
|
|
19693
19993
|
const Db = loadDatabaseCtor2();
|
|
@@ -19883,6 +20183,7 @@ class SQLiteMemoryProvider {
|
|
|
19883
20183
|
this.db = null;
|
|
19884
20184
|
this.ftsAvailable = false;
|
|
19885
20185
|
this.initialized = false;
|
|
20186
|
+
this.initPromise = null;
|
|
19886
20187
|
this.lastAutomaticJsonlMigration = null;
|
|
19887
20188
|
}
|
|
19888
20189
|
async importJsonl() {
|
|
@@ -20019,8 +20320,6 @@ class SQLiteMemoryProvider {
|
|
|
20019
20320
|
try {
|
|
20020
20321
|
if (!this.hasMigration(FTS_SCHEMA_MIGRATION_NAME)) {
|
|
20021
20322
|
this.recreateFtsIndex();
|
|
20022
|
-
this.markMigration(FTS_SCHEMA_MIGRATION_VERSION, FTS_SCHEMA_MIGRATION_NAME);
|
|
20023
|
-
this.insertEvent("migration", String(FTS_SCHEMA_MIGRATION_VERSION), FTS_SCHEMA_MIGRATION_NAME);
|
|
20024
20323
|
} else {
|
|
20025
20324
|
db.run(`CREATE VIRTUAL TABLE IF NOT EXISTS ${FTS_TABLE_NAME} USING fts5(
|
|
20026
20325
|
${ftsCreateColumnsSql()}
|
|
@@ -20353,12 +20652,60 @@ class SQLiteMemoryProvider {
|
|
|
20353
20652
|
}
|
|
20354
20653
|
requireDb() {
|
|
20355
20654
|
if (!this.db)
|
|
20356
|
-
throw new
|
|
20655
|
+
throw new MemoryValidationError("SQLite memory provider is not initialized", "provider_not_initialized");
|
|
20357
20656
|
return this.db;
|
|
20358
20657
|
}
|
|
20359
20658
|
}
|
|
20360
20659
|
function splitSql(sql) {
|
|
20361
|
-
|
|
20660
|
+
const statements = [];
|
|
20661
|
+
let current = "";
|
|
20662
|
+
let inSingleQuote = false;
|
|
20663
|
+
let inLineComment = false;
|
|
20664
|
+
for (let i = 0;i < sql.length; i++) {
|
|
20665
|
+
const char = sql[i];
|
|
20666
|
+
const next = sql[i + 1];
|
|
20667
|
+
if (inLineComment) {
|
|
20668
|
+
if (char === `
|
|
20669
|
+
`) {
|
|
20670
|
+
inLineComment = false;
|
|
20671
|
+
}
|
|
20672
|
+
continue;
|
|
20673
|
+
}
|
|
20674
|
+
if (inSingleQuote) {
|
|
20675
|
+
if (char === "'" && next === "'") {
|
|
20676
|
+
current += "''";
|
|
20677
|
+
i++;
|
|
20678
|
+
continue;
|
|
20679
|
+
}
|
|
20680
|
+
current += char;
|
|
20681
|
+
if (char === "'") {
|
|
20682
|
+
inSingleQuote = false;
|
|
20683
|
+
}
|
|
20684
|
+
continue;
|
|
20685
|
+
}
|
|
20686
|
+
if (char === "-" && next === "-") {
|
|
20687
|
+
inLineComment = true;
|
|
20688
|
+
i++;
|
|
20689
|
+
continue;
|
|
20690
|
+
}
|
|
20691
|
+
if (char === "'") {
|
|
20692
|
+
inSingleQuote = true;
|
|
20693
|
+
current += char;
|
|
20694
|
+
continue;
|
|
20695
|
+
}
|
|
20696
|
+
if (char === ";") {
|
|
20697
|
+
const trimmed2 = current.trim();
|
|
20698
|
+
if (trimmed2)
|
|
20699
|
+
statements.push(trimmed2);
|
|
20700
|
+
current = "";
|
|
20701
|
+
continue;
|
|
20702
|
+
}
|
|
20703
|
+
current += char;
|
|
20704
|
+
}
|
|
20705
|
+
const trimmed = current.trim();
|
|
20706
|
+
if (trimmed)
|
|
20707
|
+
statements.push(trimmed);
|
|
20708
|
+
return statements;
|
|
20362
20709
|
}
|
|
20363
20710
|
var FTS_STOP_WORDS = new Set([
|
|
20364
20711
|
"a",
|
|
@@ -20834,7 +21181,7 @@ async function handleMemoryStatusCommand(directory, _args) {
|
|
|
20834
21181
|
`- Provider: \`${config.provider}\``,
|
|
20835
21182
|
`- Storage: \`${storageDir}\``,
|
|
20836
21183
|
`- SQLite path: \`${sqlitePath}\``,
|
|
20837
|
-
`- SQLite database exists: \`${
|
|
21184
|
+
`- SQLite database exists: \`${existsSync25(sqlitePath)}\``,
|
|
20838
21185
|
`- Automatic destructive cleanup: \`disabled\``,
|
|
20839
21186
|
"",
|
|
20840
21187
|
"### Legacy JSONL"
|
|
@@ -21087,7 +21434,16 @@ function parseEvaluateArgs(directory, args) {
|
|
|
21087
21434
|
error: "Usage: /swarm memory evaluate [--json] [--fixtures <directory>]"
|
|
21088
21435
|
};
|
|
21089
21436
|
}
|
|
21090
|
-
|
|
21437
|
+
const resolvedFixtures = path42.resolve(directory, next);
|
|
21438
|
+
const canonical = path42.normalize(resolvedFixtures) + path42.sep;
|
|
21439
|
+
const allowedRootA = path42.normalize(directory) + path42.sep;
|
|
21440
|
+
const allowedRootB = path42.normalize(path42.join(PACKAGE_ROOT, "tests", "fixtures", "memory-recall")) + path42.sep;
|
|
21441
|
+
if (!canonical.startsWith(allowedRootA) && !canonical.startsWith(allowedRootB)) {
|
|
21442
|
+
return {
|
|
21443
|
+
error: "--fixtures <directory> must resolve under the project directory or the bundled tests/fixtures/memory-recall directory"
|
|
21444
|
+
};
|
|
21445
|
+
}
|
|
21446
|
+
fixtureDirectory = resolvedFixtures;
|
|
21091
21447
|
i++;
|
|
21092
21448
|
continue;
|
|
21093
21449
|
}
|
|
@@ -24373,19 +24729,19 @@ function hasCompoundTestExtension(filename) {
|
|
|
24373
24729
|
const lower = filename.toLowerCase();
|
|
24374
24730
|
return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
24375
24731
|
}
|
|
24376
|
-
function isLanguageSpecificTestFile(
|
|
24377
|
-
const lower =
|
|
24732
|
+
function isLanguageSpecificTestFile(basename9) {
|
|
24733
|
+
const lower = basename9.toLowerCase();
|
|
24378
24734
|
if (lower.endsWith("_test.go"))
|
|
24379
24735
|
return true;
|
|
24380
24736
|
if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
|
|
24381
24737
|
return true;
|
|
24382
24738
|
if (lower.endsWith("_spec.rb"))
|
|
24383
24739
|
return true;
|
|
24384
|
-
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(
|
|
24740
|
+
if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename9) || basename9.endsWith("Test.java") || basename9.endsWith("Tests.java") || lower.endsWith("it.java")))
|
|
24385
24741
|
return true;
|
|
24386
24742
|
if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
|
|
24387
24743
|
return true;
|
|
24388
|
-
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(
|
|
24744
|
+
if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename9) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
|
|
24389
24745
|
return true;
|
|
24390
24746
|
if (lower.endsWith(".tests.ps1"))
|
|
24391
24747
|
return true;
|
|
@@ -24393,23 +24749,23 @@ function isLanguageSpecificTestFile(basename8) {
|
|
|
24393
24749
|
}
|
|
24394
24750
|
function isConventionTestFilePath(filePath) {
|
|
24395
24751
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
24396
|
-
const
|
|
24397
|
-
return hasCompoundTestExtension(
|
|
24752
|
+
const basename9 = path48.basename(filePath);
|
|
24753
|
+
return hasCompoundTestExtension(basename9) || basename9.includes(".spec.") || basename9.includes(".test.") || isLanguageSpecificTestFile(basename9) || isTestDirectoryPath(normalizedPath);
|
|
24398
24754
|
}
|
|
24399
24755
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
24400
24756
|
const testFiles = [];
|
|
24401
24757
|
for (const file of sourceFiles) {
|
|
24402
24758
|
const absoluteFile = resolveWorkspacePath(file, workingDir);
|
|
24403
24759
|
const relativeFile = path48.relative(workingDir, absoluteFile);
|
|
24404
|
-
const
|
|
24760
|
+
const basename9 = path48.basename(absoluteFile);
|
|
24405
24761
|
const dirname23 = path48.dirname(absoluteFile);
|
|
24406
24762
|
const preferRelativeOutput = !path48.isAbsolute(file);
|
|
24407
24763
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
|
|
24408
24764
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
24409
24765
|
continue;
|
|
24410
24766
|
}
|
|
24411
|
-
const nameWithoutExt =
|
|
24412
|
-
const ext = path48.extname(
|
|
24767
|
+
const nameWithoutExt = basename9.replace(/\.[^.]+$/, "");
|
|
24768
|
+
const ext = path48.extname(basename9);
|
|
24413
24769
|
const genericTestNames = [
|
|
24414
24770
|
`${nameWithoutExt}.spec${ext}`,
|
|
24415
24771
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -24420,7 +24776,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
24420
24776
|
...languageSpecificTestNames
|
|
24421
24777
|
].map((candidateName) => path48.join(dirname23, candidateName));
|
|
24422
24778
|
const testDirectoryNames = [
|
|
24423
|
-
|
|
24779
|
+
basename9,
|
|
24424
24780
|
...genericTestNames,
|
|
24425
24781
|
...languageSpecificTestNames
|
|
24426
24782
|
];
|
|
@@ -27258,7 +27614,9 @@ async function handleResetCommand(directory, args) {
|
|
|
27258
27614
|
fs25.unlinkSync(rootPath);
|
|
27259
27615
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
27260
27616
|
}
|
|
27261
|
-
} catch {
|
|
27617
|
+
} catch (err) {
|
|
27618
|
+
results.push(`- \u274C Failed to delete ${filename}: ${err instanceof Error ? err.message : String(err)}`);
|
|
27619
|
+
}
|
|
27262
27620
|
}
|
|
27263
27621
|
try {
|
|
27264
27622
|
resetAutomationManager();
|
|
@@ -27781,6 +28139,11 @@ function resetPrmSessionState(session, sessionId) {
|
|
|
27781
28139
|
}
|
|
27782
28140
|
|
|
27783
28141
|
// src/commands/reset-session.ts
|
|
28142
|
+
function errorMessage(err) {
|
|
28143
|
+
if (err instanceof Error)
|
|
28144
|
+
return err.message;
|
|
28145
|
+
return String(err);
|
|
28146
|
+
}
|
|
27784
28147
|
async function handleResetSessionCommand(directory, _args) {
|
|
27785
28148
|
const results = [];
|
|
27786
28149
|
try {
|
|
@@ -27794,22 +28157,30 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
27794
28157
|
} catch {
|
|
27795
28158
|
results.push("\u274C Failed to delete state.json");
|
|
27796
28159
|
}
|
|
27797
|
-
|
|
27798
|
-
|
|
27799
|
-
|
|
27800
|
-
|
|
27801
|
-
|
|
27802
|
-
|
|
27803
|
-
|
|
27804
|
-
const filePath = path52.join(sessionDir, file);
|
|
27805
|
-
if (fs27.lstatSync(filePath).isFile()) {
|
|
27806
|
-
fs27.unlinkSync(filePath);
|
|
27807
|
-
deletedCount++;
|
|
27808
|
-
}
|
|
27809
|
-
}
|
|
27810
|
-
results.push(`\u2705 Cleaned ${deletedCount} additional session file(s)`);
|
|
28160
|
+
const sessionDir = path52.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
28161
|
+
let sessionFiles = [];
|
|
28162
|
+
if (fs27.existsSync(sessionDir)) {
|
|
28163
|
+
try {
|
|
28164
|
+
sessionFiles = fs27.readdirSync(sessionDir);
|
|
28165
|
+
} catch (err) {
|
|
28166
|
+
results.push(`\u274C Failed to read session directory: ${errorMessage(err)}`);
|
|
27811
28167
|
}
|
|
27812
|
-
}
|
|
28168
|
+
}
|
|
28169
|
+
for (const file of sessionFiles) {
|
|
28170
|
+
if (file === "state.json")
|
|
28171
|
+
continue;
|
|
28172
|
+
const filePath = path52.join(sessionDir, file);
|
|
28173
|
+
try {
|
|
28174
|
+
if (!fs27.existsSync(filePath))
|
|
28175
|
+
continue;
|
|
28176
|
+
if (!fs27.lstatSync(filePath).isFile())
|
|
28177
|
+
continue;
|
|
28178
|
+
fs27.unlinkSync(filePath);
|
|
28179
|
+
results.push(`\u2713 Deleted ${file}`);
|
|
28180
|
+
} catch (err) {
|
|
28181
|
+
results.push(`\u274C Failed to delete ${file}: ${errorMessage(err)}`);
|
|
28182
|
+
}
|
|
28183
|
+
}
|
|
27813
28184
|
const sessionCount = swarmState.agentSessions.size;
|
|
27814
28185
|
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
27815
28186
|
resetPrmSessionState(session, sessionId);
|
|
@@ -27971,6 +28342,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
27971
28342
|
]);
|
|
27972
28343
|
const successes = [];
|
|
27973
28344
|
const failures = [];
|
|
28345
|
+
const warnings = [];
|
|
27974
28346
|
for (const file of checkpointFiles) {
|
|
27975
28347
|
if (EXCLUDE_FILES.has(file) || file.startsWith("plan-ledger.archived-")) {
|
|
27976
28348
|
continue;
|
|
@@ -27996,30 +28368,39 @@ async function handleRollbackCommand(directory, args) {
|
|
|
27996
28368
|
`);
|
|
27997
28369
|
}
|
|
27998
28370
|
const existingLedgerPath = path54.join(swarmDir, "plan-ledger.jsonl");
|
|
28371
|
+
let ledgerDeletionFailed = false;
|
|
27999
28372
|
if (fs28.existsSync(existingLedgerPath)) {
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
|
|
28006
|
-
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
28007
|
-
const planId = derivePlanId(plan);
|
|
28008
|
-
const planHash = computePlanHash(plan);
|
|
28009
|
-
await initLedger(directory, planId, planHash, plan);
|
|
28010
|
-
await appendLedgerEvent(directory, {
|
|
28011
|
-
event_type: "plan_rebuilt",
|
|
28012
|
-
source: "rollback",
|
|
28013
|
-
plan_id: planId
|
|
28014
|
-
});
|
|
28373
|
+
try {
|
|
28374
|
+
fs28.unlinkSync(existingLedgerPath);
|
|
28375
|
+
} catch (err) {
|
|
28376
|
+
ledgerDeletionFailed = true;
|
|
28377
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
28378
|
+
warnings.push(`\u26A0\uFE0F Warning: Could not delete stale ledger (${errMsg}). The ledger may be inconsistent with the restored plan. Run /swarm reset-session to clean up session state.`);
|
|
28015
28379
|
}
|
|
28016
|
-
}
|
|
28017
|
-
|
|
28018
|
-
|
|
28019
|
-
|
|
28020
|
-
|
|
28021
|
-
|
|
28380
|
+
}
|
|
28381
|
+
if (!ledgerDeletionFailed) {
|
|
28382
|
+
try {
|
|
28383
|
+
const planJsonPath = path54.join(swarmDir, "plan.json");
|
|
28384
|
+
if (fs28.existsSync(planJsonPath)) {
|
|
28385
|
+
const planRaw = fs28.readFileSync(planJsonPath, "utf-8");
|
|
28386
|
+
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
28387
|
+
const planId = derivePlanId(plan);
|
|
28388
|
+
const planHash = computePlanHash(plan);
|
|
28389
|
+
await initLedger(directory, planId, planHash, plan);
|
|
28390
|
+
await appendLedgerEvent(directory, {
|
|
28391
|
+
event_type: "plan_rebuilt",
|
|
28392
|
+
source: "rollback",
|
|
28393
|
+
plan_id: planId
|
|
28394
|
+
});
|
|
28395
|
+
}
|
|
28396
|
+
} catch (initError) {
|
|
28397
|
+
return [
|
|
28398
|
+
`Rollback restored files but failed to initialize ledger: ${initError instanceof Error ? initError.message : String(initError)}`,
|
|
28399
|
+
"The .swarm/plan.json has been restored but the ledger may be out of sync.",
|
|
28400
|
+
"Run /swarm reset-session to reinitialize the ledger."
|
|
28401
|
+
].join(`
|
|
28022
28402
|
`);
|
|
28403
|
+
}
|
|
28023
28404
|
}
|
|
28024
28405
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
28025
28406
|
const rollbackEvent = {
|
|
@@ -28034,6 +28415,14 @@ async function handleRollbackCommand(directory, args) {
|
|
|
28034
28415
|
} catch (error2) {
|
|
28035
28416
|
console.error("Failed to write rollback event:", error2 instanceof Error ? error2.message : String(error2));
|
|
28036
28417
|
}
|
|
28418
|
+
if (warnings.length > 0) {
|
|
28419
|
+
return [
|
|
28420
|
+
...warnings,
|
|
28421
|
+
"",
|
|
28422
|
+
`Rolled back to phase ${targetPhase}: ${checkpoint2.label || "no label"}`
|
|
28423
|
+
].join(`
|
|
28424
|
+
`);
|
|
28425
|
+
}
|
|
28037
28426
|
return `Rolled back to phase ${targetPhase}: ${checkpoint2.label || "no label"}`;
|
|
28038
28427
|
}
|
|
28039
28428
|
|
|
@@ -28203,6 +28592,7 @@ async function handleSddCommand(_directory, _args) {
|
|
|
28203
28592
|
}
|
|
28204
28593
|
|
|
28205
28594
|
// src/commands/simulate.ts
|
|
28595
|
+
import { renameSync as renameSync11, unlinkSync as unlinkSync11 } from "fs";
|
|
28206
28596
|
async function handleSimulateCommand(directory, args) {
|
|
28207
28597
|
const thresholdIndex = args.indexOf("--threshold");
|
|
28208
28598
|
const minCommitsIndex = args.indexOf("--min-commits");
|
|
@@ -28254,7 +28644,16 @@ Ensure this is a git repository with commit history.`;
|
|
|
28254
28644
|
const path55 = await import("path");
|
|
28255
28645
|
const reportPath = path55.join(directory, ".swarm", "simulate-report.md");
|
|
28256
28646
|
await fs29.mkdir(path55.dirname(reportPath), { recursive: true });
|
|
28257
|
-
|
|
28647
|
+
const reportTempPath = path55.join(path55.dirname(reportPath), `${path55.basename(reportPath)}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
28648
|
+
try {
|
|
28649
|
+
await fs29.writeFile(reportTempPath, report, "utf-8");
|
|
28650
|
+
renameSync11(reportTempPath, reportPath);
|
|
28651
|
+
} catch (err) {
|
|
28652
|
+
try {
|
|
28653
|
+
unlinkSync11(reportTempPath);
|
|
28654
|
+
} catch {}
|
|
28655
|
+
throw err;
|
|
28656
|
+
}
|
|
28258
28657
|
} catch (err) {
|
|
28259
28658
|
const writeErr = err instanceof Error ? err.message : String(err);
|
|
28260
28659
|
warn(`simulate: failed to write report to ${directory}/.swarm/simulate-report.md`, writeErr);
|
|
@@ -29151,7 +29550,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
29151
29550
|
async function handleHelpCommand(ctx) {
|
|
29152
29551
|
const targetCommand = ctx.args.join(" ");
|
|
29153
29552
|
if (!targetCommand) {
|
|
29154
|
-
const { buildHelpText } = await import("./index-
|
|
29553
|
+
const { buildHelpText } = await import("./index-gjdq4na6.js");
|
|
29155
29554
|
return buildHelpText();
|
|
29156
29555
|
}
|
|
29157
29556
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -29160,7 +29559,7 @@ async function handleHelpCommand(ctx) {
|
|
|
29160
29559
|
return _internals44.buildDetailedHelp(resolved.key, resolved.entry);
|
|
29161
29560
|
}
|
|
29162
29561
|
const similar = _internals44.findSimilarCommands(targetCommand);
|
|
29163
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
29562
|
+
const { buildHelpText: fullHelp } = await import("./index-gjdq4na6.js");
|
|
29164
29563
|
if (similar.length > 0) {
|
|
29165
29564
|
return `Command '/swarm ${targetCommand}' not found.
|
|
29166
29565
|
|
|
@@ -29293,7 +29692,7 @@ var COMMAND_REGISTRY = {
|
|
|
29293
29692
|
},
|
|
29294
29693
|
"guardrail explain": {
|
|
29295
29694
|
handler: async (ctx) => {
|
|
29296
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
29695
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-9fngqx80.js");
|
|
29297
29696
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
29298
29697
|
},
|
|
29299
29698
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|