@cleocode/cleo 2026.3.0 → 2026.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -758,6 +758,65 @@ var init_node_sqlite_adapter = __esm({
758
758
  }
759
759
  });
760
760
 
761
+ // src/store/sqlite-backup.ts
762
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, statSync, unlinkSync } from "node:fs";
763
+ import { join as join3 } from "node:path";
764
+ function formatTimestamp(d) {
765
+ const pad = (n, len = 2) => String(n).padStart(len, "0");
766
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
767
+ }
768
+ function rotateSnapshots(backupDir) {
769
+ try {
770
+ const files = readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join3(backupDir, f), mtimeMs: statSync(join3(backupDir, f)).mtimeMs })).sort((a, b) => a.mtimeMs - b.mtimeMs);
771
+ while (files.length >= MAX_SNAPSHOTS) {
772
+ const oldest = files.shift();
773
+ unlinkSync(oldest.path);
774
+ }
775
+ } catch {
776
+ }
777
+ }
778
+ async function vacuumIntoBackup(opts = {}) {
779
+ const now = Date.now();
780
+ if (!opts.force && now - _lastBackupEpoch < DEBOUNCE_MS) {
781
+ return;
782
+ }
783
+ try {
784
+ const cleoDir = getCleoDir(opts.cwd);
785
+ const backupDir = join3(cleoDir, "backups", "sqlite");
786
+ mkdirSync2(backupDir, { recursive: true });
787
+ const db = getNativeDb();
788
+ if (!db) return;
789
+ const dest = join3(backupDir, `tasks-${formatTimestamp(/* @__PURE__ */ new Date())}.db`);
790
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
791
+ rotateSnapshots(backupDir);
792
+ const safeDest = dest.replace(/'/g, "''");
793
+ db.exec(`VACUUM INTO '${safeDest}'`);
794
+ _lastBackupEpoch = Date.now();
795
+ } catch {
796
+ }
797
+ }
798
+ function listSqliteBackups(cwd) {
799
+ try {
800
+ const cleoDir = getCleoDir(cwd);
801
+ const backupDir = join3(cleoDir, "backups", "sqlite");
802
+ if (!existsSync2(backupDir)) return [];
803
+ return readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join3(backupDir, f), mtimeMs: statSync(join3(backupDir, f)).mtimeMs })).sort((a, b) => b.mtimeMs - a.mtimeMs);
804
+ } catch {
805
+ return [];
806
+ }
807
+ }
808
+ var MAX_SNAPSHOTS, DEBOUNCE_MS, _lastBackupEpoch;
809
+ var init_sqlite_backup = __esm({
810
+ "src/store/sqlite-backup.ts"() {
811
+ "use strict";
812
+ init_paths();
813
+ init_sqlite();
814
+ MAX_SNAPSHOTS = 10;
815
+ DEBOUNCE_MS = 3e4;
816
+ _lastBackupEpoch = 0;
817
+ }
818
+ });
819
+
761
820
  // src/store/sqlite.ts
762
821
  var sqlite_exports = {};
763
822
  __export(sqlite_exports, {
@@ -773,16 +832,74 @@ __export(sqlite_exports, {
773
832
  resolveMigrationsFolder: () => resolveMigrationsFolder,
774
833
  schema: () => schema_exports
775
834
  });
776
- import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "node:fs";
835
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, copyFileSync, unlinkSync as unlinkSync2, renameSync as renameSync2 } from "node:fs";
777
836
  import { createRequire as createRequire2 } from "node:module";
778
- import { dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
837
+ import { dirname as dirname3, join as join4, resolve as resolve3 } from "node:path";
779
838
  import { fileURLToPath } from "node:url";
780
839
  import { eq } from "drizzle-orm";
781
840
  import { readMigrationFiles } from "drizzle-orm/migrator";
782
841
  import { drizzle } from "drizzle-orm/sqlite-proxy";
783
842
  import { migrate } from "drizzle-orm/sqlite-proxy/migrator";
784
843
  function getDbPath(cwd) {
785
- return join3(getCleoDirAbsolute(cwd), DB_FILENAME);
844
+ return join4(getCleoDirAbsolute(cwd), DB_FILENAME);
845
+ }
846
+ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
847
+ const log5 = getLogger("sqlite");
848
+ try {
849
+ const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
850
+ const taskCount = countResult?.cnt ?? 0;
851
+ if (taskCount > 0) return;
852
+ const backups = listSqliteBackups(cwd);
853
+ if (backups.length === 0) {
854
+ return;
855
+ }
856
+ const newestBackup = backups[0];
857
+ const backupDb = new DatabaseSync2(newestBackup.path, { readOnly: true });
858
+ let backupTaskCount = 0;
859
+ try {
860
+ const backupCount = backupDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
861
+ backupTaskCount = backupCount?.cnt ?? 0;
862
+ } finally {
863
+ backupDb.close();
864
+ }
865
+ if (backupTaskCount < MIN_BACKUP_TASK_COUNT) {
866
+ return;
867
+ }
868
+ log5.warn(
869
+ { dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
870
+ `Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
871
+ );
872
+ nativeDb.close();
873
+ const walPath = dbPath + "-wal";
874
+ const shmPath = dbPath + "-shm";
875
+ try {
876
+ unlinkSync2(walPath);
877
+ } catch {
878
+ }
879
+ try {
880
+ unlinkSync2(shmPath);
881
+ } catch {
882
+ }
883
+ const tempPath = dbPath + ".recovery-tmp";
884
+ copyFileSync(newestBackup.path, tempPath);
885
+ renameSync2(tempPath, dbPath);
886
+ log5.info(
887
+ { dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
888
+ "Database auto-recovered from backup successfully."
889
+ );
890
+ const restoredNativeDb = openNativeDatabase(dbPath);
891
+ _nativeDb = restoredNativeDb;
892
+ const callback = createDrizzleCallback(restoredNativeDb);
893
+ const batchCb = createBatchCallback(restoredNativeDb);
894
+ const restoredDb = drizzle(callback, batchCb, { schema: schema_exports });
895
+ await runMigrations(restoredNativeDb, restoredDb);
896
+ _db = restoredDb;
897
+ } catch (err) {
898
+ log5.error(
899
+ { err, dbPath },
900
+ "Auto-recovery from backup failed. Continuing with empty database."
901
+ );
902
+ }
786
903
  }
787
904
  async function getDb(cwd) {
788
905
  const requestedPath = getDbPath(cwd);
@@ -794,7 +911,7 @@ async function getDb(cwd) {
794
911
  _initPromise = (async () => {
795
912
  const dbPath = requestedPath;
796
913
  _dbPath = dbPath;
797
- mkdirSync2(dirname3(dbPath), { recursive: true });
914
+ mkdirSync3(dirname3(dbPath), { recursive: true });
798
915
  const nativeDb = openNativeDatabase(dbPath);
799
916
  _nativeDb = nativeDb;
800
917
  const callback = createDrizzleCallback(nativeDb);
@@ -807,19 +924,28 @@ async function getDb(cwd) {
807
924
  nativeDb.exec(
808
925
  `INSERT OR IGNORE INTO schema_meta (key, value) VALUES ('task_id_sequence', '{"counter":0,"lastId":"T000","checksum":"seed"}')`
809
926
  );
927
+ await autoRecoverFromBackup(nativeDb, dbPath, cwd);
810
928
  if (!_gitTrackingChecked) {
811
929
  _gitTrackingChecked = true;
812
930
  try {
813
931
  const { execFileSync: execFileSync9 } = await import("node:child_process");
814
- execFileSync9("git", ["ls-files", "--error-unmatch", dbPath], {
815
- cwd: resolve3(dbPath, "..", ".."),
816
- stdio: "pipe"
817
- });
932
+ const gitCwd = resolve3(dbPath, "..", "..");
933
+ const filesToCheck = [dbPath, dbPath + "-wal", dbPath + "-shm"];
818
934
  const log5 = getLogger("sqlite");
819
- log5.warn(
820
- { dbPath },
821
- "tasks.db is tracked by project git \u2014 this risks data loss on git operations. Run: git rm --cached .cleo/tasks.db (see ADR-013)"
822
- );
935
+ for (const fileToCheck of filesToCheck) {
936
+ try {
937
+ execFileSync9("git", ["ls-files", "--error-unmatch", fileToCheck], {
938
+ cwd: gitCwd,
939
+ stdio: "pipe"
940
+ });
941
+ const basename10 = fileToCheck.split("/").pop();
942
+ log5.warn(
943
+ { path: fileToCheck },
944
+ `${basename10} is tracked by project git \u2014 this risks data loss on branch switch. Run: git rm --cached ${fileToCheck.replace(gitCwd + "/", "")} (see ADR-013, T5188)`
945
+ );
946
+ } catch {
947
+ }
948
+ }
823
949
  } catch {
824
950
  }
825
951
  }
@@ -835,7 +961,7 @@ async function getDb(cwd) {
835
961
  function resolveMigrationsFolder() {
836
962
  const __filename = fileURLToPath(import.meta.url);
837
963
  const __dirname2 = dirname3(__filename);
838
- return join3(__dirname2, "..", "..", "drizzle");
964
+ return join4(__dirname2, "..", "..", "drizzle");
839
965
  }
840
966
  function tableExists(nativeDb, tableName) {
841
967
  const result = nativeDb.prepare(
@@ -929,12 +1055,12 @@ async function getSchemaVersion(cwd) {
929
1055
  return result[0]?.value ?? null;
930
1056
  }
931
1057
  function dbExists(cwd) {
932
- return existsSync2(getDbPath(cwd));
1058
+ return existsSync3(getDbPath(cwd));
933
1059
  }
934
1060
  function getNativeDb() {
935
1061
  return _nativeDb;
936
1062
  }
937
- var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
1063
+ var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MIN_BACKUP_TASK_COUNT, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
938
1064
  var init_sqlite = __esm({
939
1065
  "src/store/sqlite.ts"() {
940
1066
  "use strict";
@@ -942,6 +1068,7 @@ var init_sqlite = __esm({
942
1068
  init_paths();
943
1069
  init_node_sqlite_adapter();
944
1070
  init_logger();
1071
+ init_sqlite_backup();
945
1072
  _require2 = createRequire2(import.meta.url);
946
1073
  ({ DatabaseSync: DatabaseSync2 } = _require2("node:sqlite"));
947
1074
  DB_FILENAME = "tasks.db";
@@ -952,6 +1079,7 @@ var init_sqlite = __esm({
952
1079
  _dbPath = null;
953
1080
  _initPromise = null;
954
1081
  _gitTrackingChecked = false;
1082
+ MIN_BACKUP_TASK_COUNT = 10;
955
1083
  MAX_MIGRATION_RETRIES = 5;
956
1084
  MIGRATION_RETRY_BASE_DELAY_MS = 100;
957
1085
  MIGRATION_RETRY_MAX_DELAY_MS = 2e3;
@@ -1182,7 +1310,7 @@ __export(atomic_exports, {
1182
1310
  });
1183
1311
  import writeFileAtomic from "write-file-atomic";
1184
1312
  import { readFile, mkdir, rename, unlink } from "node:fs/promises";
1185
- import { existsSync as existsSync3 } from "node:fs";
1313
+ import { existsSync as existsSync4 } from "node:fs";
1186
1314
  import { dirname as dirname4 } from "node:path";
1187
1315
  async function atomicWrite(filePath, data, options) {
1188
1316
  try {
@@ -1220,37 +1348,37 @@ async function atomicWriteJson(filePath, data, options) {
1220
1348
  async function atomicDatabaseMigration(dbPath, tempPath, validateFn) {
1221
1349
  const backupPath = `${dbPath}.backup`;
1222
1350
  try {
1223
- if (!existsSync3(tempPath)) {
1351
+ if (!existsSync4(tempPath)) {
1224
1352
  throw new Error(`Temp database not found: ${tempPath}`);
1225
1353
  }
1226
1354
  const isValid = await validateFn(tempPath);
1227
1355
  if (!isValid) {
1228
1356
  throw new Error(`Temp database validation failed: ${tempPath}`);
1229
1357
  }
1230
- if (existsSync3(dbPath)) {
1358
+ if (existsSync4(dbPath)) {
1231
1359
  await rename(dbPath, backupPath);
1232
1360
  }
1233
1361
  await rename(tempPath, dbPath);
1234
1362
  return {
1235
1363
  success: true,
1236
1364
  tempPath,
1237
- backupPath: existsSync3(backupPath) ? backupPath : void 0
1365
+ backupPath: existsSync4(backupPath) ? backupPath : void 0
1238
1366
  };
1239
1367
  } catch (err) {
1240
1368
  return {
1241
1369
  success: false,
1242
1370
  tempPath,
1243
- backupPath: existsSync3(backupPath) ? backupPath : void 0,
1371
+ backupPath: existsSync4(backupPath) ? backupPath : void 0,
1244
1372
  error: String(err)
1245
1373
  };
1246
1374
  }
1247
1375
  }
1248
1376
  async function restoreDatabaseFromBackup(dbPath, backupPath) {
1249
1377
  try {
1250
- if (!existsSync3(backupPath)) {
1378
+ if (!existsSync4(backupPath)) {
1251
1379
  return false;
1252
1380
  }
1253
- if (existsSync3(dbPath)) {
1381
+ if (existsSync4(dbPath)) {
1254
1382
  await unlink(dbPath);
1255
1383
  }
1256
1384
  await rename(backupPath, dbPath);
@@ -1261,7 +1389,7 @@ async function restoreDatabaseFromBackup(dbPath, backupPath) {
1261
1389
  }
1262
1390
  async function cleanupMigrationArtifacts(backupPath) {
1263
1391
  try {
1264
- if (existsSync3(backupPath)) {
1392
+ if (existsSync4(backupPath)) {
1265
1393
  await unlink(backupPath);
1266
1394
  }
1267
1395
  return true;
@@ -1295,7 +1423,7 @@ var init_atomic = __esm({
1295
1423
 
1296
1424
  // src/store/backup.ts
1297
1425
  import { copyFile, rename as fsRename, readdir, unlink as unlink2, stat, mkdir as mkdir2 } from "node:fs/promises";
1298
- import { join as join4, basename } from "node:path";
1426
+ import { join as join5, basename } from "node:path";
1299
1427
  async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUPS) {
1300
1428
  try {
1301
1429
  await mkdir2(backupDir, { recursive: true });
@@ -1309,14 +1437,14 @@ async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUP
1309
1437
  );
1310
1438
  }
1311
1439
  for (let i = maxBackups; i >= 1; i--) {
1312
- const current = join4(backupDir, `${fileName}.${i}`);
1440
+ const current = join5(backupDir, `${fileName}.${i}`);
1313
1441
  if (i === maxBackups) {
1314
1442
  try {
1315
1443
  await unlink2(current);
1316
1444
  } catch {
1317
1445
  }
1318
1446
  } else {
1319
- const next = join4(backupDir, `${fileName}.${i + 1}`);
1447
+ const next = join5(backupDir, `${fileName}.${i + 1}`);
1320
1448
  try {
1321
1449
  await stat(current);
1322
1450
  await fsRename(current, next);
@@ -1324,7 +1452,7 @@ async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUP
1324
1452
  }
1325
1453
  }
1326
1454
  }
1327
- const backupPath = join4(backupDir, `${fileName}.1`);
1455
+ const backupPath = join5(backupDir, `${fileName}.1`);
1328
1456
  await copyFile(filePath, backupPath);
1329
1457
  return backupPath;
1330
1458
  } catch (err) {
@@ -1344,7 +1472,7 @@ async function listBackups(fileName, backupDir) {
1344
1472
  const numA = parseInt(a.slice(prefix.length), 10);
1345
1473
  const numB = parseInt(b.slice(prefix.length), 10);
1346
1474
  return numA - numB;
1347
- }).map((e) => join4(backupDir, e));
1475
+ }).map((e) => join5(backupDir, e));
1348
1476
  } catch {
1349
1477
  return [];
1350
1478
  }
@@ -2119,15 +2247,15 @@ var init_sqlite_data_accessor = __esm({
2119
2247
 
2120
2248
  // src/store/git-checkpoint.ts
2121
2249
  import { readFile as readFile2, writeFile } from "node:fs/promises";
2122
- import { existsSync as existsSync4 } from "node:fs";
2250
+ import { existsSync as existsSync5 } from "node:fs";
2123
2251
  import { execFile } from "node:child_process";
2124
2252
  import { promisify } from "node:util";
2125
- import { join as join5, resolve as resolve4 } from "node:path";
2253
+ import { join as join6, resolve as resolve4 } from "node:path";
2126
2254
  function makeCleoGitEnv(cleoDir) {
2127
2255
  const abs = resolve4(cleoDir);
2128
2256
  return {
2129
2257
  ...process.env,
2130
- GIT_DIR: join5(abs, ".git"),
2258
+ GIT_DIR: join6(abs, ".git"),
2131
2259
  GIT_WORK_TREE: abs
2132
2260
  };
2133
2261
  }
@@ -2145,7 +2273,7 @@ async function cleoGitCommand(args, cleoDir) {
2145
2273
  }
2146
2274
  }
2147
2275
  function isCleoGitInitialized(cleoDir) {
2148
- return existsSync4(join5(cleoDir, ".git", "HEAD"));
2276
+ return existsSync5(join6(cleoDir, ".git", "HEAD"));
2149
2277
  }
2150
2278
  async function loadCheckpointConfig(cwd) {
2151
2279
  try {
@@ -2172,25 +2300,25 @@ async function isCleoGitRepo(cleoDir) {
2172
2300
  return result.success && (result.stdout === "true" || isCleoGitInitialized(cleoDir));
2173
2301
  }
2174
2302
  function isMergeInProgress(cleoDir) {
2175
- return existsSync4(join5(cleoDir, ".git", "MERGE_HEAD"));
2303
+ return existsSync5(join6(cleoDir, ".git", "MERGE_HEAD"));
2176
2304
  }
2177
2305
  async function isDetachedHead(cleoDir) {
2178
2306
  const result = await cleoGitCommand(["symbolic-ref", "HEAD"], cleoDir);
2179
2307
  return !result.success;
2180
2308
  }
2181
2309
  function isRebaseInProgress(cleoDir) {
2182
- return existsSync4(join5(cleoDir, ".git", "rebase-merge")) || existsSync4(join5(cleoDir, ".git", "rebase-apply"));
2310
+ return existsSync5(join6(cleoDir, ".git", "rebase-merge")) || existsSync5(join6(cleoDir, ".git", "rebase-apply"));
2183
2311
  }
2184
2312
  async function recordCheckpointTime(cleoDir) {
2185
2313
  try {
2186
- const stateFile = join5(cleoDir, CHECKPOINT_STATE_FILE);
2314
+ const stateFile = join6(cleoDir, CHECKPOINT_STATE_FILE);
2187
2315
  await writeFile(stateFile, String(Math.floor(Date.now() / 1e3)));
2188
2316
  } catch {
2189
2317
  }
2190
2318
  }
2191
2319
  async function getLastCheckpointTime(cleoDir) {
2192
2320
  try {
2193
- const stateFile = join5(cleoDir, CHECKPOINT_STATE_FILE);
2321
+ const stateFile = join6(cleoDir, CHECKPOINT_STATE_FILE);
2194
2322
  const content = await readFile2(stateFile, "utf-8");
2195
2323
  const epoch = parseInt(content.trim(), 10);
2196
2324
  return isNaN(epoch) ? 0 : epoch;
@@ -2201,8 +2329,8 @@ async function getLastCheckpointTime(cleoDir) {
2201
2329
  async function getChangedStateFiles(cleoDir) {
2202
2330
  const changed = [];
2203
2331
  for (const stateFile of STATE_FILES) {
2204
- const fullPath = join5(cleoDir, stateFile);
2205
- if (!existsSync4(fullPath)) continue;
2332
+ const fullPath = join6(cleoDir, stateFile);
2333
+ if (!existsSync5(fullPath)) continue;
2206
2334
  const diffResult = await cleoGitCommand(["diff", "--quiet", "--", stateFile], cleoDir);
2207
2335
  const cachedResult = await cleoGitCommand(["diff", "--cached", "--quiet", "--", stateFile], cleoDir);
2208
2336
  const untrackedResult = await cleoGitCommand(
@@ -2226,7 +2354,7 @@ async function shouldCheckpoint(options) {
2226
2354
  const config = await loadCheckpointConfig(cwd);
2227
2355
  if (!config.enabled) return false;
2228
2356
  const cleoDir = getCleoDir(cwd);
2229
- if (!existsSync4(cleoDir)) return false;
2357
+ if (!existsSync5(cleoDir)) return false;
2230
2358
  if (!isCleoGitInitialized(cleoDir)) return false;
2231
2359
  if (!await isCleoGitRepo(cleoDir)) return false;
2232
2360
  if (isMergeInProgress(cleoDir)) return false;
@@ -2324,55 +2452,6 @@ var init_git_checkpoint = __esm({
2324
2452
  }
2325
2453
  });
2326
2454
 
2327
- // src/store/sqlite-backup.ts
2328
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync, statSync, unlinkSync } from "node:fs";
2329
- import { join as join6 } from "node:path";
2330
- function formatTimestamp(d) {
2331
- const pad = (n, len = 2) => String(n).padStart(len, "0");
2332
- return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
2333
- }
2334
- function rotateSnapshots(backupDir) {
2335
- try {
2336
- const files = readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join6(backupDir, f), mtimeMs: statSync(join6(backupDir, f)).mtimeMs })).sort((a, b) => a.mtimeMs - b.mtimeMs);
2337
- while (files.length >= MAX_SNAPSHOTS) {
2338
- const oldest = files.shift();
2339
- unlinkSync(oldest.path);
2340
- }
2341
- } catch {
2342
- }
2343
- }
2344
- async function vacuumIntoBackup(opts = {}) {
2345
- const now = Date.now();
2346
- if (!opts.force && now - _lastBackupEpoch < DEBOUNCE_MS) {
2347
- return;
2348
- }
2349
- try {
2350
- const cleoDir = getCleoDir(opts.cwd);
2351
- const backupDir = join6(cleoDir, "backups", "sqlite");
2352
- mkdirSync3(backupDir, { recursive: true });
2353
- const db = getNativeDb();
2354
- if (!db) return;
2355
- const dest = join6(backupDir, `tasks-${formatTimestamp(/* @__PURE__ */ new Date())}.db`);
2356
- db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
2357
- rotateSnapshots(backupDir);
2358
- const safeDest = dest.replace(/'/g, "''");
2359
- db.exec(`VACUUM INTO '${safeDest}'`);
2360
- _lastBackupEpoch = Date.now();
2361
- } catch {
2362
- }
2363
- }
2364
- var MAX_SNAPSHOTS, DEBOUNCE_MS, _lastBackupEpoch;
2365
- var init_sqlite_backup = __esm({
2366
- "src/store/sqlite-backup.ts"() {
2367
- "use strict";
2368
- init_paths();
2369
- init_sqlite();
2370
- MAX_SNAPSHOTS = 10;
2371
- DEBOUNCE_MS = 3e4;
2372
- _lastBackupEpoch = 0;
2373
- }
2374
- });
2375
-
2376
2455
  // src/core/sequence/index.ts
2377
2456
  var sequence_exports = {};
2378
2457
  __export(sequence_exports, {
@@ -2381,7 +2460,7 @@ __export(sequence_exports, {
2381
2460
  repairSequence: () => repairSequence,
2382
2461
  showSequence: () => showSequence
2383
2462
  });
2384
- import { existsSync as existsSync6, readFileSync as readFileSync2, renameSync as renameSync2 } from "node:fs";
2463
+ import { existsSync as existsSync6, readFileSync as readFileSync2, renameSync as renameSync3 } from "node:fs";
2385
2464
  import { join as join7 } from "node:path";
2386
2465
  import { eq as eq4 } from "drizzle-orm";
2387
2466
  function getLegacySequenceJsonPath(cwd) {
@@ -2412,10 +2491,10 @@ function renameLegacyFile(path) {
2412
2491
  const migratedPath = `${path}.migrated`;
2413
2492
  try {
2414
2493
  if (!existsSync6(migratedPath)) {
2415
- renameSync2(path, migratedPath);
2494
+ renameSync3(path, migratedPath);
2416
2495
  return;
2417
2496
  }
2418
- renameSync2(path, `${migratedPath}.${Date.now()}`);
2497
+ renameSync3(path, `${migratedPath}.${Date.now()}`);
2419
2498
  } catch {
2420
2499
  }
2421
2500
  }
@@ -5331,7 +5410,7 @@ var init_find = __esm({
5331
5410
  });
5332
5411
 
5333
5412
  // src/store/file-utils.ts
5334
- import { readFileSync as readFileSync3, writeFileSync, renameSync as renameSync3, existsSync as existsSync8, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "node:fs";
5413
+ import { readFileSync as readFileSync3, writeFileSync, renameSync as renameSync4, existsSync as existsSync8, unlinkSync as unlinkSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "node:fs";
5335
5414
  import { join as join8, dirname as dirname6, basename as basename2 } from "node:path";
5336
5415
  import { randomBytes as randomBytes2 } from "node:crypto";
5337
5416
  import * as lockfile2 from "proper-lockfile";
@@ -5346,13 +5425,13 @@ function rotateBackup(filePath) {
5346
5425
  const current = join8(backupDir, `${name}.${i}`);
5347
5426
  if (i === MAX_BACKUPS) {
5348
5427
  try {
5349
- unlinkSync2(current);
5428
+ unlinkSync3(current);
5350
5429
  } catch {
5351
5430
  }
5352
5431
  } else {
5353
5432
  const next = join8(backupDir, `${name}.${i + 1}`);
5354
5433
  try {
5355
- if (existsSync8(current)) renameSync3(current, next);
5434
+ if (existsSync8(current)) renameSync4(current, next);
5356
5435
  } catch {
5357
5436
  }
5358
5437
  }
@@ -5372,10 +5451,10 @@ function writeJsonFileAtomic(filePath, data, indent = 2) {
5372
5451
  if (existsSync8(filePath)) {
5373
5452
  rotateBackup(filePath);
5374
5453
  }
5375
- renameSync3(tempPath, filePath);
5454
+ renameSync4(tempPath, filePath);
5376
5455
  } catch (error) {
5377
5456
  try {
5378
- unlinkSync2(tempPath);
5457
+ unlinkSync3(tempPath);
5379
5458
  } catch {
5380
5459
  }
5381
5460
  throw error;
@@ -6860,7 +6939,7 @@ import {
6860
6939
  writeFileSync as writeFileSync2,
6861
6940
  mkdirSync as mkdirSync7,
6862
6941
  readdirSync as readdirSync3,
6863
- copyFileSync,
6942
+ copyFileSync as copyFileSync2,
6864
6943
  statSync as statSync3,
6865
6944
  rmSync
6866
6945
  } from "node:fs";
@@ -6936,7 +7015,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
6936
7015
  for (const sf of readdirSync3(srcPath)) {
6937
7016
  if (!copiedFiles.has(sf)) {
6938
7017
  try {
6939
- copyFileSync(join13(srcPath, sf), join13(dstPath, sf));
7018
+ copyFileSync2(join13(srcPath, sf), join13(dstPath, sf));
6940
7019
  copiedFiles.add(sf);
6941
7020
  count2++;
6942
7021
  } catch {
@@ -6944,7 +7023,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
6944
7023
  }
6945
7024
  }
6946
7025
  } else if (!copiedFiles.has(entry)) {
6947
- copyFileSync(srcPath, dstPath);
7026
+ copyFileSync2(srcPath, dstPath);
6948
7027
  copiedFiles.add(entry);
6949
7028
  count2++;
6950
7029
  }
@@ -10474,7 +10553,6 @@ var init_relates = __esm({
10474
10553
  init_paths();
10475
10554
  init_errors();
10476
10555
  init_exit_codes();
10477
- init_data_safety_central();
10478
10556
  }
10479
10557
  });
10480
10558
 
@@ -15330,7 +15408,7 @@ function getMigrationStatus(projectRoot, opts) {
15330
15408
  // src/core/system/cleanup.ts
15331
15409
  init_errors();
15332
15410
  init_exit_codes();
15333
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as unlinkSync3 } from "node:fs";
15411
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "node:fs";
15334
15412
  import { join as join17 } from "node:path";
15335
15413
  function cleanupSystem(projectRoot, params) {
15336
15414
  if (!params.target) {
@@ -15381,11 +15459,11 @@ function cleanupSystem(projectRoot, params) {
15381
15459
  if (params.olderThan && meta.timestamp < params.olderThan) {
15382
15460
  items.push(file.replace(".meta.json", ""));
15383
15461
  if (!dryRun) {
15384
- unlinkSync3(metaFilePath);
15462
+ unlinkSync4(metaFilePath);
15385
15463
  for (const bf of readdirSync4(fullDir)) {
15386
15464
  if (bf.includes(meta.backupId)) {
15387
15465
  try {
15388
- unlinkSync3(join17(fullDir, bf));
15466
+ unlinkSync4(join17(fullDir, bf));
15389
15467
  } catch {
15390
15468
  }
15391
15469
  }
@@ -15410,7 +15488,7 @@ function cleanupSystem(projectRoot, params) {
15410
15488
  items.push(file);
15411
15489
  if (!dryRun) {
15412
15490
  try {
15413
- unlinkSync3(join17(cleoDir, file));
15491
+ unlinkSync4(join17(cleoDir, file));
15414
15492
  } catch {
15415
15493
  }
15416
15494
  }
@@ -30211,7 +30289,7 @@ function registerPlanCommand(program2) {
30211
30289
  }
30212
30290
 
30213
30291
  // src/core/otel/index.ts
30214
- import { readFileSync as readFileSync41, existsSync as existsSync55, writeFileSync as writeFileSync14, copyFileSync as copyFileSync2 } from "node:fs";
30292
+ import { readFileSync as readFileSync41, existsSync as existsSync55, writeFileSync as writeFileSync14, copyFileSync as copyFileSync3 } from "node:fs";
30215
30293
  import { join as join59 } from "node:path";
30216
30294
  function getProjectRoot2() {
30217
30295
  let dir = process.cwd();
@@ -30310,7 +30388,7 @@ async function clearOtelData() {
30310
30388
  const tokenFile = getTokenFilePath();
30311
30389
  if (existsSync55(tokenFile)) {
30312
30390
  const backup = `${tokenFile}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
30313
- copyFileSync2(tokenFile, backup);
30391
+ copyFileSync3(tokenFile, backup);
30314
30392
  writeFileSync14(tokenFile, "");
30315
30393
  return { message: "Token tracking cleared", backup };
30316
30394
  }
@@ -30701,7 +30779,7 @@ init_paths();
30701
30779
 
30702
30780
  // src/core/upgrade.ts
30703
30781
  init_paths();
30704
- import { existsSync as existsSync59, readFileSync as readFileSync43, writeFileSync as writeFileSync15, mkdirSync as mkdirSync19, readdirSync as readdirSync18, copyFileSync as copyFileSync3 } from "node:fs";
30782
+ import { existsSync as existsSync59, readFileSync as readFileSync43, writeFileSync as writeFileSync15, mkdirSync as mkdirSync19, readdirSync as readdirSync18, copyFileSync as copyFileSync4 } from "node:fs";
30705
30783
  import { join as join63 } from "node:path";
30706
30784
  init_agent_outputs();
30707
30785
 
@@ -30901,7 +30979,7 @@ var MigrationLogger = class {
30901
30979
  if (!existsSync56(logsDir)) {
30902
30980
  return;
30903
30981
  }
30904
- const { readdirSync: readdirSync20, unlinkSync: unlinkSync4 } = __require("node:fs");
30982
+ const { readdirSync: readdirSync20, unlinkSync: unlinkSync5 } = __require("node:fs");
30905
30983
  const files = readdirSync20(logsDir).filter((f) => f.startsWith("migration-") && f.endsWith(".jsonl")).map((f) => ({
30906
30984
  name: f,
30907
30985
  path: join60(logsDir, f),
@@ -30910,7 +30988,7 @@ var MigrationLogger = class {
30910
30988
  const filesToRemove = files.slice(this.config.maxLogFiles);
30911
30989
  for (const file of filesToRemove) {
30912
30990
  try {
30913
- unlinkSync4(file.path);
30991
+ unlinkSync5(file.path);
30914
30992
  } catch {
30915
30993
  }
30916
30994
  }
@@ -31235,7 +31313,7 @@ async function runUpgrade(options = {}) {
31235
31313
  if (!existsSync59(backupDir)) {
31236
31314
  mkdirSync19(backupDir, { recursive: true });
31237
31315
  }
31238
- copyFileSync3(dbPath2, dbBackupPath);
31316
+ copyFileSync4(dbPath2, dbBackupPath);
31239
31317
  const { createHash: createHash8 } = await import("node:crypto");
31240
31318
  const origChecksum = createHash8("sha256").update(readFileSync43(dbPath2)).digest("hex");
31241
31319
  const backupChecksum = createHash8("sha256").update(readFileSync43(dbBackupPath)).digest("hex");
@@ -31254,8 +31332,8 @@ async function runUpgrade(options = {}) {
31254
31332
  logger.info("backup", "verified", "Backup integrity verified", { checksum: origChecksum });
31255
31333
  }
31256
31334
  if (existsSync59(dbTempPath)) {
31257
- const { unlinkSync: unlinkSync4 } = await import("node:fs");
31258
- unlinkSync4(dbTempPath);
31335
+ const { unlinkSync: unlinkSync5 } = await import("node:fs");
31336
+ unlinkSync5(dbTempPath);
31259
31337
  }
31260
31338
  const configPath = join63(cleoDir2, "config.json");
31261
31339
  let configBackup = null;
@@ -31287,7 +31365,7 @@ async function runUpgrade(options = {}) {
31287
31365
  if (result.success) {
31288
31366
  const totalImported = result.tasksImported + result.archivedImported;
31289
31367
  if (totalImported === 0 && existsSync59(dbBackupPath)) {
31290
- copyFileSync3(dbBackupPath, dbPath2);
31368
+ copyFileSync4(dbBackupPath, dbPath2);
31291
31369
  if (configBackup) {
31292
31370
  writeFileSync15(configPath, configBackup);
31293
31371
  }
@@ -31329,7 +31407,7 @@ async function runUpgrade(options = {}) {
31329
31407
  }
31330
31408
  } else {
31331
31409
  if (existsSync59(dbBackupPath)) {
31332
- copyFileSync3(dbBackupPath, dbPath2);
31410
+ copyFileSync4(dbBackupPath, dbPath2);
31333
31411
  }
31334
31412
  if (configBackup) {
31335
31413
  writeFileSync15(configPath, configBackup);
@@ -31356,7 +31434,7 @@ async function runUpgrade(options = {}) {
31356
31434
  if (existsSync59(safetyDir)) {
31357
31435
  const backups = readdirSync18(safetyDir).filter((f) => f.startsWith("tasks.db.pre-migration.")).sort().reverse();
31358
31436
  if (backups.length > 0 && !existsSync59(dbPath2)) {
31359
- copyFileSync3(join63(safetyDir, backups[0]), dbPath2);
31437
+ copyFileSync4(join63(safetyDir, backups[0]), dbPath2);
31360
31438
  }
31361
31439
  }
31362
31440
  } catch {
@@ -31445,11 +31523,11 @@ async function runUpgrade(options = {}) {
31445
31523
  mkdirSync19(backupDir, { recursive: true });
31446
31524
  for (const f of foundStale) {
31447
31525
  const src = join63(cleoDir, f);
31448
- copyFileSync3(src, join63(backupDir, f));
31526
+ copyFileSync4(src, join63(backupDir, f));
31449
31527
  }
31450
- const { unlinkSync: unlinkSync4 } = await import("node:fs");
31528
+ const { unlinkSync: unlinkSync5 } = await import("node:fs");
31451
31529
  for (const f of foundStale) {
31452
- unlinkSync4(join63(cleoDir, f));
31530
+ unlinkSync5(join63(cleoDir, f));
31453
31531
  }
31454
31532
  actions.push({
31455
31533
  action: "stale_json_cleanup",