@onebrain-ai/cli 2.0.4 → 2.0.6
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/onebrain +147 -210
- package/package.json +1 -1
package/dist/onebrain
CHANGED
|
@@ -10210,14 +10210,18 @@ var init_register_hooks = __esm(() => {
|
|
|
10210
10210
|
};
|
|
10211
10211
|
HOOK_EVENTS = ["Stop", "PreCompact", "PostCompact", "SessionStart"];
|
|
10212
10212
|
PERMISSIONS_TO_ADD = [
|
|
10213
|
-
"Bash(onebrain
|
|
10214
|
-
"Bash(bun install -g @onebrain-ai/cli
|
|
10215
|
-
"Bash(npm install -g @onebrain-ai/cli
|
|
10213
|
+
"Bash(onebrain *)",
|
|
10214
|
+
"Bash(bun install -g @onebrain-ai/cli*)",
|
|
10215
|
+
"Bash(npm install -g @onebrain-ai/cli*)"
|
|
10216
10216
|
];
|
|
10217
10217
|
BUN_BIN = join4(homedir2(), ".bun", "bin");
|
|
10218
10218
|
NPM_GLOBAL_BIN = join4(homedir2(), ".npm-global", "bin");
|
|
10219
10219
|
});
|
|
10220
10220
|
|
|
10221
|
+
// src/index.ts
|
|
10222
|
+
import { existsSync } from "fs";
|
|
10223
|
+
import { dirname as dirname6, join as join13 } from "path";
|
|
10224
|
+
|
|
10221
10225
|
// ../../node_modules/commander/esm.mjs
|
|
10222
10226
|
var import__ = __toESM(require_commander(), 1);
|
|
10223
10227
|
var {
|
|
@@ -10431,7 +10435,8 @@ async function checkQmdEmbeddings(config) {
|
|
|
10431
10435
|
};
|
|
10432
10436
|
}
|
|
10433
10437
|
try {
|
|
10434
|
-
const
|
|
10438
|
+
const qmdArgs = process.platform === "win32" ? ["powershell.exe", "-NoProfile", "-Command", "qmd status --json"] : ["qmd", "status", "--json"];
|
|
10439
|
+
const proc = Bun.spawn(qmdArgs, {
|
|
10435
10440
|
stdout: "pipe",
|
|
10436
10441
|
stderr: "pipe"
|
|
10437
10442
|
});
|
|
@@ -10742,7 +10747,7 @@ init_dist();
|
|
|
10742
10747
|
import { mkdir as mkdir3, readFile as readFile3, rename as rename3, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
|
|
10743
10748
|
import { homedir as homedir3 } from "node:os";
|
|
10744
10749
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
10745
|
-
var binaryVersion = "2.0.
|
|
10750
|
+
var binaryVersion = "2.0.6";
|
|
10746
10751
|
var STANDARD_FOLDERS = [
|
|
10747
10752
|
"00-inbox",
|
|
10748
10753
|
"01-projects",
|
|
@@ -11023,7 +11028,7 @@ async function initCommand(opts = {}) {
|
|
|
11023
11028
|
// src/commands/update.ts
|
|
11024
11029
|
init_dist3();
|
|
11025
11030
|
init_dist();
|
|
11026
|
-
import { readFile as readFile4, rename as rename4, writeFile as writeFile4 } from "node:fs/promises";
|
|
11031
|
+
import { access, readFile as readFile4, rename as rename4, writeFile as writeFile4 } from "node:fs/promises";
|
|
11027
11032
|
import { join as join6 } from "node:path";
|
|
11028
11033
|
var GITHUB_RELEASES_URL = "https://api.github.com/repos/kengio/onebrain/releases/latest";
|
|
11029
11034
|
function resolveBranch2(channel) {
|
|
@@ -11060,9 +11065,22 @@ async function fetchLatestVersion(fetchFn) {
|
|
|
11060
11065
|
}
|
|
11061
11066
|
return tagName;
|
|
11062
11067
|
}
|
|
11068
|
+
var _windowsShell;
|
|
11069
|
+
function windowsShell() {
|
|
11070
|
+
if (_windowsShell !== undefined)
|
|
11071
|
+
return _windowsShell;
|
|
11072
|
+
try {
|
|
11073
|
+
const r2 = Bun.spawnSync(["pwsh", "--version"], { stdout: "pipe", stderr: "pipe" });
|
|
11074
|
+
_windowsShell = r2.exitCode === 0 ? "pwsh" : "powershell.exe";
|
|
11075
|
+
} catch {
|
|
11076
|
+
_windowsShell = "powershell.exe";
|
|
11077
|
+
}
|
|
11078
|
+
return _windowsShell;
|
|
11079
|
+
}
|
|
11063
11080
|
async function defaultInstallBinary(version) {
|
|
11064
11081
|
const isWindows = process.platform === "win32";
|
|
11065
|
-
const
|
|
11082
|
+
const safeVersion = version.replace(/'/g, "''");
|
|
11083
|
+
const cmd = isWindows ? [windowsShell(), "-NoProfile", "-Command", `npm install -g '@onebrain-ai/cli@${safeVersion}'`] : ["bun", "install", "-g", `@onebrain-ai/cli@${version}`];
|
|
11066
11084
|
const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
11067
11085
|
const exitCode = await proc.exited;
|
|
11068
11086
|
if (exitCode !== 0) {
|
|
@@ -11072,7 +11090,9 @@ async function defaultInstallBinary(version) {
|
|
|
11072
11090
|
}
|
|
11073
11091
|
async function defaultValidateBinary() {
|
|
11074
11092
|
try {
|
|
11075
|
-
const
|
|
11093
|
+
const isWindows = process.platform === "win32";
|
|
11094
|
+
const cmd = isWindows ? [windowsShell(), "-NoProfile", "-Command", "onebrain --version"] : ["onebrain", "--version"];
|
|
11095
|
+
const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
11076
11096
|
const exitCode = await proc.exited;
|
|
11077
11097
|
if (exitCode !== 0)
|
|
11078
11098
|
return false;
|
|
@@ -11119,6 +11139,19 @@ async function runUpdate(opts = {}) {
|
|
|
11119
11139
|
} else {
|
|
11120
11140
|
writeLine("OneBrain Update");
|
|
11121
11141
|
}
|
|
11142
|
+
try {
|
|
11143
|
+
await access(join6(vaultDir, "vault.yml"));
|
|
11144
|
+
} catch {
|
|
11145
|
+
const msg = `vault.yml not found in ${vaultDir}. Run 'onebrain update' from inside an OneBrain vault.`;
|
|
11146
|
+
if (isTTY) {
|
|
11147
|
+
v2.error(msg);
|
|
11148
|
+
} else {
|
|
11149
|
+
writeLine(`error: ${msg}`);
|
|
11150
|
+
}
|
|
11151
|
+
result.error = msg;
|
|
11152
|
+
result.exitCode = 1;
|
|
11153
|
+
return result;
|
|
11154
|
+
}
|
|
11122
11155
|
let latestVersion;
|
|
11123
11156
|
try {
|
|
11124
11157
|
latestVersion = await fetchLatestVersion(fetchFn);
|
|
@@ -11149,30 +11182,47 @@ async function runUpdate(opts = {}) {
|
|
|
11149
11182
|
}
|
|
11150
11183
|
let filesAdded = 0;
|
|
11151
11184
|
let filesRemoved = 0;
|
|
11185
|
+
const syncSpinner = isTTY ? L2() : null;
|
|
11186
|
+
syncSpinner?.start("Syncing plugin files…");
|
|
11152
11187
|
try {
|
|
11153
11188
|
const syncResult = await vaultSyncFn(vaultDir, { branch });
|
|
11154
11189
|
filesAdded = syncResult.filesAdded;
|
|
11155
11190
|
filesRemoved = syncResult.filesRemoved;
|
|
11191
|
+
syncSpinner?.stop(`Synced — ${filesAdded} added, ${filesRemoved} removed`);
|
|
11192
|
+
if (!isTTY) {
|
|
11193
|
+
writeLine(`syncing: ${filesAdded} files synced, ${filesRemoved} removed`);
|
|
11194
|
+
}
|
|
11156
11195
|
} catch (err) {
|
|
11157
11196
|
const msg = err instanceof Error ? err.message : String(err);
|
|
11197
|
+
syncSpinner?.stop("Sync failed");
|
|
11158
11198
|
result.error = `vault-sync failed: ${msg}`;
|
|
11159
11199
|
result.exitCode = 1;
|
|
11160
11200
|
process.stderr.write(`update: ${result.error}
|
|
11161
11201
|
`);
|
|
11162
11202
|
return result;
|
|
11163
11203
|
}
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11171
|
-
|
|
11204
|
+
const needsBinaryUpdate = latestVersion !== currentVersion;
|
|
11205
|
+
if (needsBinaryUpdate) {
|
|
11206
|
+
const installSpinner = isTTY ? L2() : null;
|
|
11207
|
+
installSpinner?.start(`Installing @onebrain-ai/cli ${latestVersion}…`);
|
|
11208
|
+
try {
|
|
11209
|
+
await installBinaryFn(latestVersion);
|
|
11210
|
+
installSpinner?.stop(`Installed @onebrain-ai/cli ${latestVersion}`);
|
|
11211
|
+
if (!isTTY) {
|
|
11212
|
+
writeLine(`upgrading: @onebrain-ai/cli ${latestVersion} installed`);
|
|
11213
|
+
}
|
|
11214
|
+
} catch (err) {
|
|
11215
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11216
|
+
installSpinner?.stop("Install failed");
|
|
11217
|
+
result.error = `Binary install failed: ${msg}`;
|
|
11218
|
+
result.exitCode = 1;
|
|
11219
|
+
process.stderr.write(`update: ${result.error}
|
|
11172
11220
|
`);
|
|
11173
|
-
|
|
11221
|
+
return result;
|
|
11222
|
+
}
|
|
11223
|
+
} else {
|
|
11224
|
+
noteStep("binary", `@onebrain-ai/cli ${latestVersion} already up to date`);
|
|
11174
11225
|
}
|
|
11175
|
-
noteStep("upgrading", `@onebrain-ai/cli ${latestVersion} installed`);
|
|
11176
11226
|
const binaryValid = await validateBinaryFn();
|
|
11177
11227
|
if (!binaryValid) {
|
|
11178
11228
|
result.error = "Binary validation failed. Check PATH. register-hooks NOT called.";
|
|
@@ -11229,7 +11279,6 @@ async function updateCommand(opts = {}) {
|
|
|
11229
11279
|
|
|
11230
11280
|
// src/internal/checkpoint.ts
|
|
11231
11281
|
import { readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
11232
|
-
import { mkdir as mkdir4, readdir as readdir2, writeFile as writeFile5 } from "node:fs/promises";
|
|
11233
11282
|
import { tmpdir as osTmpdir } from "node:os";
|
|
11234
11283
|
import { join as join7 } from "node:path";
|
|
11235
11284
|
var SKIP_WINDOW = 60;
|
|
@@ -11251,15 +11300,13 @@ function readState(token, tmpDir = osTmpdir()) {
|
|
|
11251
11300
|
const count = Number(parts[0]);
|
|
11252
11301
|
const last_ts = Number(parts[1]);
|
|
11253
11302
|
const last_stop_nn = parts[2] ?? "00";
|
|
11254
|
-
const pending_stub = parts[3] && parts[3].length > 0 ? parts[3] : undefined;
|
|
11255
11303
|
if (!Number.isInteger(count) || !Number.isInteger(last_ts) || !/^\d{2}$/.test(last_stop_nn)) {
|
|
11256
11304
|
throw new Error("malformed state");
|
|
11257
11305
|
}
|
|
11258
|
-
return { count, last_ts, last_stop_nn
|
|
11306
|
+
return { count, last_ts, last_stop_nn };
|
|
11259
11307
|
} catch {
|
|
11260
|
-
const now = Math.floor(Date.now() / 1000);
|
|
11261
11308
|
try {
|
|
11262
|
-
writeFileSync(stateFilePath(token, tmpDir),
|
|
11309
|
+
writeFileSync(stateFilePath(token, tmpDir), "0:0:00", "utf8");
|
|
11263
11310
|
} catch (writeErr) {
|
|
11264
11311
|
process.stderr.write(`checkpoint: failed to rewrite state file for token ${token}: ${writeErr}
|
|
11265
11312
|
`);
|
|
@@ -11273,8 +11320,7 @@ function readState(token, tmpDir = osTmpdir()) {
|
|
|
11273
11320
|
}
|
|
11274
11321
|
function writeState(token, state, tmpDir = osTmpdir()) {
|
|
11275
11322
|
const path = stateFilePath(token, tmpDir);
|
|
11276
|
-
const
|
|
11277
|
-
const content = state.pending_stub !== undefined ? `${base}:${state.pending_stub}` : base;
|
|
11323
|
+
const content = `${state.count}:${state.last_ts}:${state.last_stop_nn}`;
|
|
11278
11324
|
try {
|
|
11279
11325
|
writeFileSync(path, content, "utf8");
|
|
11280
11326
|
} catch (err) {
|
|
@@ -11341,12 +11387,6 @@ function formatDate(epochSeconds) {
|
|
|
11341
11387
|
const dd = String(d2.getDate()).padStart(2, "0");
|
|
11342
11388
|
return `${yyyy}-${mm}-${dd}`;
|
|
11343
11389
|
}
|
|
11344
|
-
function formatYYYY(epochSeconds) {
|
|
11345
|
-
return new Date(epochSeconds * 1000).getFullYear().toString();
|
|
11346
|
-
}
|
|
11347
|
-
function formatMM(epochSeconds) {
|
|
11348
|
-
return String(new Date(epochSeconds * 1000).getMonth() + 1).padStart(2, "0");
|
|
11349
|
-
}
|
|
11350
11390
|
function emitBlock(reason) {
|
|
11351
11391
|
process.stdout.write(`${JSON.stringify({ decision: "block", reason })}
|
|
11352
11392
|
`);
|
|
@@ -11378,156 +11418,26 @@ function handleStop(token, vaultRoot, now = Math.floor(Date.now() / 1000), tmpDi
|
|
|
11378
11418
|
const since = maxNn === 0 ? " since start" : ` since checkpoint-${String(maxNn).padStart(2, "0")}`;
|
|
11379
11419
|
const filename = `${date}-${token}-checkpoint-${nextNn}.md`;
|
|
11380
11420
|
emitBlock(`${filename}${since}`);
|
|
11381
|
-
writeState(token, { count: 0, last_ts: now, last_stop_nn: nextNn
|
|
11421
|
+
writeState(token, { count: 0, last_ts: now, last_stop_nn: nextNn }, tmpDir);
|
|
11382
11422
|
}
|
|
11383
|
-
|
|
11384
|
-
tags: [checkpoint, session-log]
|
|
11385
|
-
date: ${date}
|
|
11386
|
-
checkpoint: ${nn}
|
|
11387
|
-
trigger: precompact
|
|
11388
|
-
merged: false
|
|
11389
|
-
---
|
|
11390
|
-
|
|
11391
|
-
## What We Worked On
|
|
11392
|
-
|
|
11393
|
-
<!-- stub: written automatically before compact — fill in via postcompact -->
|
|
11394
|
-
|
|
11395
|
-
## Key Decisions
|
|
11396
|
-
|
|
11397
|
-
-
|
|
11398
|
-
|
|
11399
|
-
## Insights & Learnings
|
|
11400
|
-
|
|
11401
|
-
-
|
|
11402
|
-
|
|
11403
|
-
## What Worked / Didn't Work
|
|
11404
|
-
|
|
11405
|
-
-
|
|
11406
|
-
|
|
11407
|
-
## Action Items
|
|
11408
|
-
|
|
11409
|
-
-
|
|
11410
|
-
|
|
11411
|
-
## Open Questions
|
|
11412
|
-
|
|
11413
|
-
-
|
|
11414
|
-
`;
|
|
11415
|
-
async function handlePrecompact(token, vaultRoot, now = Math.floor(Date.now() / 1000), tmpDir = osTmpdir()) {
|
|
11423
|
+
function handlePrecompact(token, _vaultRoot, now = Math.floor(Date.now() / 1000), tmpDir = osTmpdir()) {
|
|
11416
11424
|
const state = readState(token, tmpDir);
|
|
11417
11425
|
if (state.last_ts > 0 && now - state.last_ts < PRECOMPACT_RECENCY) {
|
|
11418
11426
|
return;
|
|
11419
11427
|
}
|
|
11420
|
-
|
|
11421
|
-
return;
|
|
11422
|
-
}
|
|
11423
|
-
const date = formatDate(now);
|
|
11424
|
-
let logsFolder = DEFAULT_LOGS_FOLDER;
|
|
11425
|
-
try {
|
|
11426
|
-
const config = await loadVaultConfig(vaultRoot);
|
|
11427
|
-
logsFolder = config.folders.logs;
|
|
11428
|
-
} catch {}
|
|
11429
|
-
const yyyy = formatYYYY(now);
|
|
11430
|
-
const mm = formatMM(now);
|
|
11431
|
-
const stubDir = join7(vaultRoot, logsFolder, yyyy, mm);
|
|
11432
|
-
const existingFiles = await readdir2(stubDir).catch(() => []);
|
|
11433
|
-
const prefix = `${date}-${token}-checkpoint-`;
|
|
11434
|
-
const maxNn = existingFiles.reduce((max, f2) => {
|
|
11435
|
-
if (!f2.startsWith(prefix) || !f2.endsWith(".md"))
|
|
11436
|
-
return max;
|
|
11437
|
-
const m3 = f2.match(/-checkpoint-(\d{2})\.md$/);
|
|
11438
|
-
return m3 ? Math.max(max, Number(m3[1])) : max;
|
|
11439
|
-
}, 0);
|
|
11440
|
-
const stubNn = String(maxNn + 1).padStart(2, "0");
|
|
11441
|
-
const stubFilename = `${date}-${token}-checkpoint-${stubNn}.md`;
|
|
11442
|
-
const stubPath = join7(stubDir, stubFilename);
|
|
11443
|
-
try {
|
|
11444
|
-
await mkdir4(stubDir, { recursive: true });
|
|
11445
|
-
await writeFile5(stubPath, PRECOMPACT_STUB_TEMPLATE(date, stubNn), "utf8");
|
|
11446
|
-
} catch (err) {
|
|
11447
|
-
process.stderr.write(`checkpoint: failed to write stub file ${stubPath}: ${err}
|
|
11448
|
-
`);
|
|
11449
|
-
return;
|
|
11450
|
-
}
|
|
11451
|
-
writeState(token, {
|
|
11452
|
-
count: 0,
|
|
11453
|
-
last_ts: state.last_ts,
|
|
11454
|
-
last_stop_nn: state.last_stop_nn,
|
|
11455
|
-
pending_stub: stubFilename
|
|
11456
|
-
}, tmpDir);
|
|
11428
|
+
writeState(token, { count: 0, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn }, tmpDir);
|
|
11457
11429
|
}
|
|
11458
|
-
function handlePostcompact(token,
|
|
11430
|
+
function handlePostcompact(token, _vaultRoot, now = Math.floor(Date.now() / 1000), tmpDir = osTmpdir()) {
|
|
11459
11431
|
const state = readState(token, tmpDir);
|
|
11460
|
-
if (
|
|
11432
|
+
if (state.last_ts > 0 && now - state.last_ts < PRECOMPACT_RECENCY) {
|
|
11461
11433
|
writeState(token, { count: 0, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn }, tmpDir);
|
|
11462
11434
|
return;
|
|
11463
11435
|
}
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
const stubNnNum = Number(stubNn);
|
|
11467
|
-
const { logsFolder } = loadVaultSettings(vaultRoot);
|
|
11468
|
-
const date = state.pending_stub.slice(0, 10);
|
|
11469
|
-
const yyyy = date.slice(0, 4);
|
|
11470
|
-
const mm = date.slice(5, 7);
|
|
11471
|
-
const dir = join7(vaultRoot, logsFolder, yyyy, mm);
|
|
11472
|
-
const prefix = `${date}-${token}-checkpoint-`;
|
|
11473
|
-
let predecessorNn = 0;
|
|
11474
|
-
try {
|
|
11475
|
-
for (const f2 of readdirSync(dir)) {
|
|
11476
|
-
if (!f2.startsWith(prefix) || !f2.endsWith(".md"))
|
|
11477
|
-
continue;
|
|
11478
|
-
const m3 = f2.match(/-checkpoint-(\d{2})\.md$/);
|
|
11479
|
-
if (m3) {
|
|
11480
|
-
const nn = Number(m3[1]);
|
|
11481
|
-
if (nn < stubNnNum)
|
|
11482
|
-
predecessorNn = Math.max(predecessorNn, nn);
|
|
11483
|
-
}
|
|
11484
|
-
}
|
|
11485
|
-
} catch {}
|
|
11486
|
-
const since = predecessorNn === 0 ? " since start" : ` since checkpoint-${String(predecessorNn).padStart(2, "0")}`;
|
|
11487
|
-
emitBlock(`fill-checkpoint: ${state.pending_stub}${since}`);
|
|
11488
|
-
writeState(token, { count: 0, last_ts: now, last_stop_nn: stubNn }, tmpDir);
|
|
11436
|
+
emitBlock(`auto-wrapup: ${token}`);
|
|
11437
|
+
writeState(token, { count: 0, last_ts: now, last_stop_nn: state.last_stop_nn }, tmpDir);
|
|
11489
11438
|
}
|
|
11490
11439
|
function postcompactFallback(token, vaultRoot, now = Math.floor(Date.now() / 1000), tmpDir = osTmpdir()) {
|
|
11491
|
-
|
|
11492
|
-
if (state.pending_stub) {
|
|
11493
|
-
handlePostcompact(token, vaultRoot, now, tmpDir);
|
|
11494
|
-
return;
|
|
11495
|
-
}
|
|
11496
|
-
const { logsFolder } = loadVaultSettings(vaultRoot);
|
|
11497
|
-
const date = formatDate(now);
|
|
11498
|
-
const yyyy = date.slice(0, 4);
|
|
11499
|
-
const mm = date.slice(5, 7);
|
|
11500
|
-
const dir = join7(vaultRoot, logsFolder, yyyy, mm);
|
|
11501
|
-
const prefix = `${date}-${token}-checkpoint-`;
|
|
11502
|
-
const stubs = [];
|
|
11503
|
-
const allNns = [];
|
|
11504
|
-
try {
|
|
11505
|
-
for (const f2 of readdirSync(dir)) {
|
|
11506
|
-
if (!f2.startsWith(prefix) || !f2.endsWith(".md"))
|
|
11507
|
-
continue;
|
|
11508
|
-
const m3 = f2.match(/-checkpoint-(\d{2})\.md$/);
|
|
11509
|
-
if (!m3)
|
|
11510
|
-
continue;
|
|
11511
|
-
allNns.push(Number(m3[1]));
|
|
11512
|
-
const content = readFileSync(join7(dir, f2), "utf8");
|
|
11513
|
-
if (/^trigger:\s*precompact/m.test(content) && !/^merged:\s*true/m.test(content)) {
|
|
11514
|
-
stubs.push(f2);
|
|
11515
|
-
}
|
|
11516
|
-
}
|
|
11517
|
-
} catch {}
|
|
11518
|
-
if (stubs.length === 0) {
|
|
11519
|
-
writeState(token, { count: 0, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn }, tmpDir);
|
|
11520
|
-
return;
|
|
11521
|
-
}
|
|
11522
|
-
stubs.sort();
|
|
11523
|
-
const stubFilename = stubs[stubs.length - 1];
|
|
11524
|
-
const stubNnMatch = stubFilename.match(/-checkpoint-(\d{2})\.md$/);
|
|
11525
|
-
const stubNn = stubNnMatch?.[1] ?? "01";
|
|
11526
|
-
const stubNnNum = Number(stubNn);
|
|
11527
|
-
const predecessorNn = allNns.filter((n) => n < stubNnNum).reduce((max, n) => Math.max(max, n), 0);
|
|
11528
|
-
const since = predecessorNn === 0 ? " since start" : ` since checkpoint-${String(predecessorNn).padStart(2, "0")}`;
|
|
11529
|
-
emitBlock(`fill-checkpoint: ${stubFilename}${since}`);
|
|
11530
|
-
writeState(token, { count: 0, last_ts: now, last_stop_nn: stubNn }, tmpDir);
|
|
11440
|
+
handlePostcompact(token, vaultRoot, now, tmpDir);
|
|
11531
11441
|
}
|
|
11532
11442
|
async function checkpointCommand(mode, token, vaultRoot) {
|
|
11533
11443
|
try {
|
|
@@ -11536,7 +11446,7 @@ async function checkpointCommand(mode, token, vaultRoot) {
|
|
|
11536
11446
|
handleStop(token, vaultRoot);
|
|
11537
11447
|
break;
|
|
11538
11448
|
case "precompact":
|
|
11539
|
-
|
|
11449
|
+
handlePrecompact(token, vaultRoot);
|
|
11540
11450
|
break;
|
|
11541
11451
|
case "postcompact":
|
|
11542
11452
|
postcompactFallback(token, vaultRoot);
|
|
@@ -11555,7 +11465,7 @@ async function checkpointCommand(mode, token, vaultRoot) {
|
|
|
11555
11465
|
}
|
|
11556
11466
|
|
|
11557
11467
|
// src/internal/migrate.ts
|
|
11558
|
-
import { readFile as readFile5, readdir as
|
|
11468
|
+
import { readFile as readFile5, readdir as readdir2, writeFile as writeFile5 } from "node:fs/promises";
|
|
11559
11469
|
import { join as join8 } from "node:path";
|
|
11560
11470
|
init_dist();
|
|
11561
11471
|
function parseFrontmatterWithRest(rawText) {
|
|
@@ -11584,7 +11494,7 @@ function parseFrontmatterWithRest(rawText) {
|
|
|
11584
11494
|
}
|
|
11585
11495
|
async function listMdFiles(dir) {
|
|
11586
11496
|
try {
|
|
11587
|
-
const entries = await
|
|
11497
|
+
const entries = await readdir2(dir);
|
|
11588
11498
|
return entries.filter((e2) => e2.endsWith(".md"));
|
|
11589
11499
|
} catch {
|
|
11590
11500
|
return [];
|
|
@@ -11596,7 +11506,7 @@ async function runBackfillRecapped(logsFolder) {
|
|
|
11596
11506
|
let skipped = 0;
|
|
11597
11507
|
let yearDirs = [];
|
|
11598
11508
|
try {
|
|
11599
|
-
yearDirs = await
|
|
11509
|
+
yearDirs = await readdir2(logsFolder);
|
|
11600
11510
|
} catch {
|
|
11601
11511
|
return { backfilled: 0, skipped: 0 };
|
|
11602
11512
|
}
|
|
@@ -11604,7 +11514,7 @@ async function runBackfillRecapped(logsFolder) {
|
|
|
11604
11514
|
const yearPath = join8(logsFolder, yearDir);
|
|
11605
11515
|
let monthDirs = [];
|
|
11606
11516
|
try {
|
|
11607
|
-
monthDirs = await
|
|
11517
|
+
monthDirs = await readdir2(yearPath);
|
|
11608
11518
|
} catch {
|
|
11609
11519
|
continue;
|
|
11610
11520
|
}
|
|
@@ -11634,7 +11544,7 @@ async function runBackfillRecapped(logsFolder) {
|
|
|
11634
11544
|
const updatedContent = `---
|
|
11635
11545
|
${updatedFm}---
|
|
11636
11546
|
${rest}`;
|
|
11637
|
-
await
|
|
11547
|
+
await writeFile5(fpath, updatedContent, "utf8");
|
|
11638
11548
|
backfilled++;
|
|
11639
11549
|
} catch (error) {
|
|
11640
11550
|
process.stderr.write(`migrate: error processing ${fname}: ${error}
|
|
@@ -11667,7 +11577,7 @@ async function migrateCommand(migrationName) {
|
|
|
11667
11577
|
|
|
11668
11578
|
// src/internal/orphan-scan.ts
|
|
11669
11579
|
init_dist();
|
|
11670
|
-
import { readFile as readFile6, readdir as
|
|
11580
|
+
import { readFile as readFile6, readdir as readdir3 } from "node:fs/promises";
|
|
11671
11581
|
import { join as join9 } from "node:path";
|
|
11672
11582
|
function parseFrontmatter(rawText) {
|
|
11673
11583
|
const text = rawText.replace(/\r\n/g, `
|
|
@@ -11696,7 +11606,7 @@ function getMonthParts(now = new Date) {
|
|
|
11696
11606
|
}
|
|
11697
11607
|
async function listMdFiles2(dir) {
|
|
11698
11608
|
try {
|
|
11699
|
-
const entries = await
|
|
11609
|
+
const entries = await readdir3(dir);
|
|
11700
11610
|
return entries.filter((e2) => e2.endsWith(".md"));
|
|
11701
11611
|
} catch {
|
|
11702
11612
|
return [];
|
|
@@ -11774,6 +11684,13 @@ async function orphanScanCommand(logsFolder, sessionToken) {
|
|
|
11774
11684
|
}
|
|
11775
11685
|
|
|
11776
11686
|
// src/internal/qmd-reindex.ts
|
|
11687
|
+
function buildQmdSpawnArgs(collection, platform = process.platform) {
|
|
11688
|
+
if (platform === "win32") {
|
|
11689
|
+
const safe = collection.replace(/'/g, "''");
|
|
11690
|
+
return ["powershell.exe", "-NoProfile", "-Command", `qmd update -c '${safe}'`];
|
|
11691
|
+
}
|
|
11692
|
+
return ["qmd", "update", "-c", collection];
|
|
11693
|
+
}
|
|
11777
11694
|
async function qmdReindexCommand(vaultRoot) {
|
|
11778
11695
|
try {
|
|
11779
11696
|
const config = await loadVaultConfig(vaultRoot);
|
|
@@ -11781,7 +11698,7 @@ async function qmdReindexCommand(vaultRoot) {
|
|
|
11781
11698
|
if (!collection) {
|
|
11782
11699
|
return;
|
|
11783
11700
|
}
|
|
11784
|
-
const proc = Bun.spawn(
|
|
11701
|
+
const proc = Bun.spawn(buildQmdSpawnArgs(collection), {
|
|
11785
11702
|
detached: true,
|
|
11786
11703
|
stdin: "ignore",
|
|
11787
11704
|
stdout: "ignore",
|
|
@@ -11797,7 +11714,7 @@ async function qmdReindexCommand(vaultRoot) {
|
|
|
11797
11714
|
// src/internal/register-hooks.ts
|
|
11798
11715
|
init_dist3();
|
|
11799
11716
|
init_dist();
|
|
11800
|
-
import { mkdir as
|
|
11717
|
+
import { mkdir as mkdir4, readFile as readFile7, rename as rename5, writeFile as writeFile6 } from "node:fs/promises";
|
|
11801
11718
|
import { homedir as homedir4 } from "node:os";
|
|
11802
11719
|
import { dirname as dirname4, join as join10 } from "node:path";
|
|
11803
11720
|
var HOOK_COMMANDS2 = {
|
|
@@ -11808,9 +11725,9 @@ var HOOK_COMMANDS2 = {
|
|
|
11808
11725
|
};
|
|
11809
11726
|
var HOOK_EVENTS2 = ["Stop", "PreCompact", "PostCompact", "SessionStart"];
|
|
11810
11727
|
var PERMISSIONS_TO_ADD2 = [
|
|
11811
|
-
"Bash(onebrain
|
|
11812
|
-
"Bash(bun install -g @onebrain-ai/cli
|
|
11813
|
-
"Bash(npm install -g @onebrain-ai/cli
|
|
11728
|
+
"Bash(onebrain *)",
|
|
11729
|
+
"Bash(bun install -g @onebrain-ai/cli*)",
|
|
11730
|
+
"Bash(npm install -g @onebrain-ai/cli*)"
|
|
11814
11731
|
];
|
|
11815
11732
|
var BUN_BIN2 = join10(homedir4(), ".bun", "bin");
|
|
11816
11733
|
var NPM_GLOBAL_BIN2 = join10(homedir4(), ".npm-global", "bin");
|
|
@@ -11827,9 +11744,9 @@ async function readSettings2(settingsPath) {
|
|
|
11827
11744
|
}
|
|
11828
11745
|
}
|
|
11829
11746
|
async function writeSettings2(settingsPath, settings) {
|
|
11830
|
-
await
|
|
11747
|
+
await mkdir4(dirname4(settingsPath), { recursive: true });
|
|
11831
11748
|
const tmpPath = `${settingsPath}.tmp`;
|
|
11832
|
-
await
|
|
11749
|
+
await writeFile6(tmpPath, JSON.stringify(settings, null, 4), "utf8");
|
|
11833
11750
|
await rename5(tmpPath, settingsPath);
|
|
11834
11751
|
}
|
|
11835
11752
|
function checkHookPresence2(groups, targetCmd) {
|
|
@@ -11958,7 +11875,7 @@ ${ONEBRAIN_MARKER2}
|
|
|
11958
11875
|
${PATH_EXPORT2}
|
|
11959
11876
|
`;
|
|
11960
11877
|
const tmpPath = `${profilePath}.tmp`;
|
|
11961
|
-
await
|
|
11878
|
+
await writeFile6(tmpPath, updated, "utf8");
|
|
11962
11879
|
await rename5(tmpPath, profilePath);
|
|
11963
11880
|
}
|
|
11964
11881
|
async function runRegisterHooks2(opts = {}) {
|
|
@@ -12164,7 +12081,8 @@ async function cleanStaleStateFile(token, tmpDir) {
|
|
|
12164
12081
|
}
|
|
12165
12082
|
async function queryQmdUnembedded() {
|
|
12166
12083
|
try {
|
|
12167
|
-
const
|
|
12084
|
+
const qmdArgs = process.platform === "win32" ? ["powershell.exe", "-NoProfile", "-Command", "qmd status --json"] : ["qmd", "status", "--json"];
|
|
12085
|
+
const proc = Bun.spawn(qmdArgs, {
|
|
12168
12086
|
stdout: "pipe",
|
|
12169
12087
|
stderr: "pipe"
|
|
12170
12088
|
});
|
|
@@ -12216,15 +12134,15 @@ async function sessionInitCommand(vaultRoot) {
|
|
|
12216
12134
|
init_dist3();
|
|
12217
12135
|
init_dist();
|
|
12218
12136
|
import {
|
|
12219
|
-
mkdir as
|
|
12137
|
+
mkdir as mkdir5,
|
|
12220
12138
|
mkdtemp as mkdtemp2,
|
|
12221
12139
|
readFile as readFile8,
|
|
12222
|
-
readdir as
|
|
12140
|
+
readdir as readdir4,
|
|
12223
12141
|
rename as rename6,
|
|
12224
12142
|
rm as rm2,
|
|
12225
12143
|
stat as stat4,
|
|
12226
12144
|
unlink as unlink3,
|
|
12227
|
-
writeFile as
|
|
12145
|
+
writeFile as writeFile7
|
|
12228
12146
|
} from "node:fs/promises";
|
|
12229
12147
|
import { homedir as homedir5, tmpdir as tmpdir2 } from "node:os";
|
|
12230
12148
|
import { dirname as dirname5, join as join12, relative as relative2 } from "node:path";
|
|
@@ -12249,7 +12167,7 @@ async function downloadTarball2(branch, fetchFn) {
|
|
|
12249
12167
|
}
|
|
12250
12168
|
async function extractTarball2(tarball, destDir) {
|
|
12251
12169
|
const tarPath = join12(destDir, "bundle.tar.gz");
|
|
12252
|
-
await
|
|
12170
|
+
await writeFile7(tarPath, Buffer.from(tarball));
|
|
12253
12171
|
const proc = Bun.spawn(["tar", "-xzf", tarPath, "-C", destDir], {
|
|
12254
12172
|
stdout: "pipe",
|
|
12255
12173
|
stderr: "pipe"
|
|
@@ -12260,7 +12178,7 @@ async function extractTarball2(tarball, destDir) {
|
|
|
12260
12178
|
throw new Error(`tar extraction failed (exit ${exitCode}): ${errText.trim()}`);
|
|
12261
12179
|
}
|
|
12262
12180
|
await unlink3(tarPath);
|
|
12263
|
-
const entries = await
|
|
12181
|
+
const entries = await readdir4(destDir);
|
|
12264
12182
|
const topLevel = entries.find((e2) => e2 !== "bundle.tar.gz");
|
|
12265
12183
|
if (!topLevel) {
|
|
12266
12184
|
throw new Error("Extracted tarball contains no top-level directory");
|
|
@@ -12274,7 +12192,7 @@ async function listFilesRecursive2(dir) {
|
|
|
12274
12192
|
const current = queue.pop();
|
|
12275
12193
|
let entries;
|
|
12276
12194
|
try {
|
|
12277
|
-
entries = await
|
|
12195
|
+
entries = await readdir4(current);
|
|
12278
12196
|
} catch {
|
|
12279
12197
|
continue;
|
|
12280
12198
|
}
|
|
@@ -12298,7 +12216,7 @@ async function listFilesRecursive2(dir) {
|
|
|
12298
12216
|
async function syncPluginFiles2(extractedDir, vaultRoot, unlinkFn = unlink3) {
|
|
12299
12217
|
const sourcePlugin = join12(extractedDir, ".claude", "plugins", "onebrain");
|
|
12300
12218
|
const destPlugin = join12(vaultRoot, ".claude", "plugins", "onebrain");
|
|
12301
|
-
await
|
|
12219
|
+
await mkdir5(destPlugin, { recursive: true });
|
|
12302
12220
|
const sourceFiles = await listFilesRecursive2(sourcePlugin);
|
|
12303
12221
|
const sourceRelSet = new Set(sourceFiles.map((f2) => relative2(sourcePlugin, f2)));
|
|
12304
12222
|
const destFiles = await listFilesRecursive2(destPlugin);
|
|
@@ -12313,9 +12231,9 @@ async function syncPluginFiles2(extractedDir, vaultRoot, unlinkFn = unlink3) {
|
|
|
12313
12231
|
for (const srcPath of sourceFiles) {
|
|
12314
12232
|
const rel = relative2(sourcePlugin, srcPath);
|
|
12315
12233
|
const destPath = join12(destPlugin, rel);
|
|
12316
|
-
await
|
|
12234
|
+
await mkdir5(dirname5(destPath), { recursive: true });
|
|
12317
12235
|
const content = await readFile8(srcPath);
|
|
12318
|
-
await
|
|
12236
|
+
await writeFile7(destPath, content);
|
|
12319
12237
|
filesAdded++;
|
|
12320
12238
|
}
|
|
12321
12239
|
let filesRemoved = 0;
|
|
@@ -12335,7 +12253,7 @@ async function copyRootDocs2(extractedDir, vaultRoot) {
|
|
|
12335
12253
|
const destPath = join12(vaultRoot, doc);
|
|
12336
12254
|
try {
|
|
12337
12255
|
const content = await readFile8(srcPath);
|
|
12338
|
-
await
|
|
12256
|
+
await writeFile7(destPath, content);
|
|
12339
12257
|
} catch {}
|
|
12340
12258
|
}
|
|
12341
12259
|
}
|
|
@@ -12352,7 +12270,7 @@ async function mergeHarnessFile2(extractedDir, vaultRoot, filename) {
|
|
|
12352
12270
|
try {
|
|
12353
12271
|
vaultText = await readFile8(destPath, "utf8");
|
|
12354
12272
|
} catch {
|
|
12355
|
-
await
|
|
12273
|
+
await writeFile7(destPath, repoText, "utf8");
|
|
12356
12274
|
return repoText.split(`
|
|
12357
12275
|
`).filter((l2) => l2.startsWith("@")).length;
|
|
12358
12276
|
}
|
|
@@ -12373,7 +12291,7 @@ async function mergeHarnessFile2(extractedDir, vaultRoot, filename) {
|
|
|
12373
12291
|
}
|
|
12374
12292
|
const merged = vaultLines.join(`
|
|
12375
12293
|
`);
|
|
12376
|
-
await
|
|
12294
|
+
await writeFile7(destPath, merged, "utf8");
|
|
12377
12295
|
return newImports.length;
|
|
12378
12296
|
}
|
|
12379
12297
|
async function mergeHarnessFiles2(extractedDir, vaultRoot) {
|
|
@@ -12398,7 +12316,7 @@ async function updateVaultYml2(vaultRoot, version, updateChannel) {
|
|
|
12398
12316
|
raw.update_channel = updateChannel;
|
|
12399
12317
|
const updated = $stringify(raw, { lineWidth: 0 });
|
|
12400
12318
|
const tmpPath = `${vaultYmlPath}.tmp`;
|
|
12401
|
-
await
|
|
12319
|
+
await writeFile7(tmpPath, updated, "utf8");
|
|
12402
12320
|
await rename6(tmpPath, vaultYmlPath);
|
|
12403
12321
|
}
|
|
12404
12322
|
async function readPluginVersion2(vaultRoot) {
|
|
@@ -12468,7 +12386,7 @@ async function pinToVault2(vaultRoot, installedPluginsPath, installedPluginsCach
|
|
|
12468
12386
|
return { skipped: false };
|
|
12469
12387
|
}
|
|
12470
12388
|
const tmpPath = `${installedPluginsPath}.tmp`;
|
|
12471
|
-
await
|
|
12389
|
+
await writeFile7(tmpPath, JSON.stringify(data, null, 4), "utf8");
|
|
12472
12390
|
await rename6(tmpPath, installedPluginsPath);
|
|
12473
12391
|
return { skipped: false };
|
|
12474
12392
|
}
|
|
@@ -12499,7 +12417,7 @@ async function cleanPluginCache2(installedPluginsPath, installedPluginsCacheDir)
|
|
|
12499
12417
|
} catch {}
|
|
12500
12418
|
if (onebrainDirs.length === 0) {
|
|
12501
12419
|
try {
|
|
12502
|
-
const marketplaceDirs = await
|
|
12420
|
+
const marketplaceDirs = await readdir4(cacheDir);
|
|
12503
12421
|
for (const mp of marketplaceDirs) {
|
|
12504
12422
|
const candidate = join12(cacheDir, mp, "onebrain");
|
|
12505
12423
|
try {
|
|
@@ -12515,7 +12433,7 @@ async function cleanPluginCache2(installedPluginsPath, installedPluginsCacheDir)
|
|
|
12515
12433
|
for (const pluginDir of onebrainDirs) {
|
|
12516
12434
|
let versionDirs;
|
|
12517
12435
|
try {
|
|
12518
|
-
versionDirs = await
|
|
12436
|
+
versionDirs = await readdir4(pluginDir);
|
|
12519
12437
|
} catch {
|
|
12520
12438
|
continue;
|
|
12521
12439
|
}
|
|
@@ -12702,14 +12620,31 @@ async function vaultSyncCommand2(vaultRoot, opts = {}) {
|
|
|
12702
12620
|
}
|
|
12703
12621
|
|
|
12704
12622
|
// src/index.ts
|
|
12705
|
-
var VERSION = "2.0.
|
|
12706
|
-
var RELEASE_DATE = "2026-04-
|
|
12623
|
+
var VERSION = "2.0.6";
|
|
12624
|
+
var RELEASE_DATE = "2026-04-26";
|
|
12625
|
+
if (process.platform === "win32") {
|
|
12626
|
+
process.stdout.setDefaultEncoding("utf8");
|
|
12627
|
+
process.stderr.setDefaultEncoding("utf8");
|
|
12628
|
+
}
|
|
12707
12629
|
var VERSION_STRING = `OneBrain v${VERSION} \u2014 released ${RELEASE_DATE}`;
|
|
12708
12630
|
if (process.argv.slice(2).length === 0) {
|
|
12709
12631
|
console.log(VERSION_STRING);
|
|
12710
12632
|
console.log("Run `onebrain help` for available commands.");
|
|
12711
12633
|
process.exit(0);
|
|
12712
12634
|
}
|
|
12635
|
+
function findVaultRoot(startDir) {
|
|
12636
|
+
if (!startDir)
|
|
12637
|
+
return process.cwd();
|
|
12638
|
+
let dir = startDir;
|
|
12639
|
+
while (true) {
|
|
12640
|
+
if (existsSync(join13(dir, "vault.yml")))
|
|
12641
|
+
return dir;
|
|
12642
|
+
const parent = dirname6(dir);
|
|
12643
|
+
if (parent === dir)
|
|
12644
|
+
return startDir;
|
|
12645
|
+
dir = parent;
|
|
12646
|
+
}
|
|
12647
|
+
}
|
|
12713
12648
|
var program2 = new Command;
|
|
12714
12649
|
program2.name("onebrain").description("OneBrain CLI \u2014 personal AI OS for Obsidian").version(VERSION_STRING, "-v, --version");
|
|
12715
12650
|
program2.command("init").description("Initialize a new OneBrain vault").option("--vault-dir <path>", "vault root directory (default: cwd)").option("--harness <harness>", "harness type: claude-code | gemini | direct").option("--force", "overwrite existing vault.yml without prompting").action(async (opts) => {
|
|
@@ -12719,29 +12654,31 @@ program2.command("init").description("Initialize a new OneBrain vault").option("
|
|
|
12719
12654
|
force: opts.force
|
|
12720
12655
|
});
|
|
12721
12656
|
});
|
|
12722
|
-
program2.command("update").description("Update OneBrain plugin files from GitHub").option("--check", "show what would change and exit without making changes").option("--channel <channel>", "update channel: stable | next").action(async (opts) => {
|
|
12657
|
+
program2.command("update").description("Update OneBrain plugin files from GitHub").option("--check", "show what would change and exit without making changes").option("--channel <channel>", "update channel: stable | next").option("--vault-dir <path>", "vault root directory (default: auto-detect from cwd)").action(async (opts) => {
|
|
12723
12658
|
await updateCommand({
|
|
12659
|
+
vaultDir: opts.vaultDir ?? findVaultRoot(process.cwd()),
|
|
12724
12660
|
check: opts.check,
|
|
12725
12661
|
channel: opts.channel
|
|
12726
12662
|
});
|
|
12727
12663
|
});
|
|
12728
12664
|
program2.command("doctor").description("Run vault health checks and report issues").action(async () => {
|
|
12729
|
-
const vaultRoot = process.cwd();
|
|
12665
|
+
const vaultRoot = findVaultRoot(process.cwd());
|
|
12730
12666
|
await doctorCommand({ vaultDir: vaultRoot, binaryVersion: VERSION });
|
|
12731
12667
|
});
|
|
12732
12668
|
program2.command("help").description("Show this help message").action(() => {
|
|
12733
12669
|
program2.help();
|
|
12734
12670
|
});
|
|
12735
|
-
program2.command("session-init", { hidden: true }).description("Emit session token and datetime (called by Claude Code hook)").action(async () => {
|
|
12736
|
-
const vaultRoot = process.cwd();
|
|
12671
|
+
program2.command("session-init", { hidden: true }).description("Emit session token and datetime (called by Claude Code hook)").option("--vault-dir <path>", "vault root directory (default: auto-detect from cwd)").action(async (opts) => {
|
|
12672
|
+
const vaultRoot = opts.vaultDir ?? findVaultRoot(process.cwd());
|
|
12737
12673
|
await sessionInitCommand(vaultRoot);
|
|
12738
12674
|
});
|
|
12739
12675
|
program2.command("orphan-scan", { hidden: true }).description("Scan for orphaned checkpoint files in logs folder").argument("<logs_folder>", "path to logs folder").argument("<session_token>", "current session token to exclude").action(async (logsFolder, sessionToken) => {
|
|
12740
12676
|
await orphanScanCommand(logsFolder, sessionToken);
|
|
12741
12677
|
});
|
|
12742
|
-
program2.command("checkpoint", { hidden: true }).description("Handle checkpoint lifecycle (stop/precompact/postcompact/reset)").argument("<mode>", "stop | precompact | postcompact | reset").action(async (mode) => {
|
|
12678
|
+
program2.command("checkpoint", { hidden: true }).description("Handle checkpoint lifecycle (stop/precompact/postcompact/reset)").argument("<mode>", "stop | precompact | postcompact | reset").option("--vault-dir <path>", "vault root directory (default: auto-detect from cwd)").action(async (mode, opts) => {
|
|
12743
12679
|
const token = await resolveSessionToken();
|
|
12744
|
-
|
|
12680
|
+
const vaultRoot = opts.vaultDir ?? findVaultRoot(process.cwd());
|
|
12681
|
+
await checkpointCommand(mode, token, vaultRoot);
|
|
12745
12682
|
});
|
|
12746
12683
|
program2.command("qmd-reindex", { hidden: true }).description("Trigger qmd index rebuild").action(async () => {
|
|
12747
12684
|
const vaultRoot = process.cwd();
|