@yemi33/minions 0.1.2115 → 0.1.2117

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.
@@ -94,9 +94,7 @@ function _readScheduleRunsFromSql(db) {
94
94
 
95
95
  function readScheduleRuns() {
96
96
  const { getDb } = require('./db');
97
- let db;
98
- try { db = getDb(); }
99
- catch { return _readJson(_resolveFilePath('schedule-runs.json')) || {}; }
97
+ const db = getDb();
100
98
  _resyncScheduleRunsIfDiverged(db);
101
99
  const out = _readScheduleRunsFromSql(db);
102
100
  if (Object.keys(out).length === 0) {
@@ -207,9 +205,7 @@ function _readPipelineRunsFromSql(db) {
207
205
 
208
206
  function readPipelineRuns() {
209
207
  const { getDb } = require('./db');
210
- let db;
211
- try { db = getDb(); }
212
- catch { return _readJson(_resolveFilePath('pipeline-runs.json')) || {}; }
208
+ const db = getDb();
213
209
  _resyncPipelineRunsIfDiverged(db);
214
210
  const out = _readPipelineRunsFromSql(db);
215
211
  if (Object.keys(out).length === 0) {
@@ -329,9 +325,7 @@ function _readManagedProcessesFromSql(db) {
329
325
 
330
326
  function readManagedProcesses() {
331
327
  const { getDb } = require('./db');
332
- let db;
333
- try { db = getDb(); }
334
- catch { return _readJson(_resolveFilePath('managed-processes.json')) || { specs: [] }; }
328
+ const db = getDb();
335
329
  _resyncManagedProcessesIfDiverged(db);
336
330
  const out = _readManagedProcessesFromSql(db);
337
331
  if (out.specs.length === 0) {
@@ -449,9 +443,7 @@ function _readWorktreePoolFromSql(db) {
449
443
 
450
444
  function readWorktreePool() {
451
445
  const { getDb } = require('./db');
452
- let db;
453
- try { db = getDb(); }
454
- catch { return _readJson(_resolveFilePath('worktree-pool.json')) || { entries: [] }; }
446
+ const db = getDb();
455
447
  _resyncWorktreePoolIfDiverged(db);
456
448
  const out = _readWorktreePoolFromSql(db);
457
449
  if (out.entries.length === 0) {
@@ -581,9 +573,7 @@ function _readQaRunsFromSqlOnly(db) {
581
573
 
582
574
  function readQaRuns() {
583
575
  const { getDb } = require('./db');
584
- let db;
585
- try { db = getDb(); }
586
- catch { return _readJson(_resolveFilePath('qa-runs.json')) || []; }
576
+ const db = getDb();
587
577
  _resyncQaRunsIfDiverged(db);
588
578
  const out = _readQaRunsFromSqlOnly(db);
589
579
  if (out.length === 0) {
@@ -741,9 +731,7 @@ function _readQaSessionsFromSqlOnly(db) {
741
731
 
742
732
  function readQaSessions() {
743
733
  const { getDb } = require('./db');
744
- let db;
745
- try { db = getDb(); }
746
- catch { return _readJson(_resolveFilePath('qa-sessions.json')) || []; }
734
+ const db = getDb();
747
735
  _resyncQaSessionsIfDiverged(db);
748
736
  const out = _readQaSessionsFromSqlOnly(db);
749
737
  if (out.length === 0) {
@@ -875,9 +863,7 @@ function _readPrLinksFromSql(db) {
875
863
 
876
864
  function readPrLinks() {
877
865
  const { getDb } = require('./db');
878
- let db;
879
- try { db = getDb(); }
880
- catch { return _readJson(_resolveFilePath('pr-links.json')) || {}; }
866
+ const db = getDb();
881
867
  try { _resyncPrLinksIfDiverged(db); }
882
868
  catch { /* table may be missing on a stale install — fall back to JSON */ }
883
869
  let out;
@@ -941,6 +927,430 @@ function _mirrorPrLinksJson(filePath) {
941
927
  } catch { /* mirror best-effort */ }
942
928
  }
943
929
 
930
+ // ─── cooldowns ─────────────────────────────────────────────────────────────
931
+ // Shape: { [key]: { timestamp, failures, ... } }
932
+ // SQL: row per key.
933
+
934
+ let _cooldownsHash = null;
935
+
936
+ function _hydrateCooldowns(db) {
937
+ const fp = _resolveFilePath('cooldowns.json');
938
+ const raw = _readJson(fp) || {};
939
+ db.prepare('DELETE FROM cooldowns').run();
940
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return;
941
+ const now = Date.now();
942
+ const ins = db.prepare('INSERT INTO cooldowns (key, data, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO NOTHING');
943
+ for (const [key, value] of Object.entries(raw)) {
944
+ ins.run(String(key), JSON.stringify(value), now);
945
+ }
946
+ }
947
+
948
+ function _resyncCooldownsIfDiverged(db) {
949
+ const fp = _resolveFilePath('cooldowns.json');
950
+ const currentHash = _fileContentHash(fp);
951
+ if (currentHash == null) return;
952
+ if (_cooldownsHash != null && currentHash === _cooldownsHash) return;
953
+ if (_cooldownsHash == null) {
954
+ const sqlHas = db.prepare('SELECT 1 FROM cooldowns LIMIT 1').get();
955
+ if (sqlHas) { _cooldownsHash = currentHash; return; }
956
+ }
957
+ _hydrateCooldowns(db);
958
+ _cooldownsHash = currentHash;
959
+ }
960
+
961
+ function _readCooldownsFromSql(db) {
962
+ const rows = db.prepare('SELECT key, data FROM cooldowns').all();
963
+ const out = {};
964
+ for (const row of rows) {
965
+ try { out[row.key] = JSON.parse(row.data); }
966
+ catch { /* skip malformed */ }
967
+ }
968
+ return out;
969
+ }
970
+
971
+ function readCooldowns() {
972
+ const { getDb } = require('./db');
973
+ const db = getDb();
974
+ try { _resyncCooldownsIfDiverged(db); }
975
+ catch { /* table may be missing on stale install */ }
976
+ let out;
977
+ try { out = _readCooldownsFromSql(db); }
978
+ catch { return _readJson(_resolveFilePath('cooldowns.json')) || {}; }
979
+ if (Object.keys(out).length === 0) {
980
+ const fallback = _readJson(_resolveFilePath('cooldowns.json'));
981
+ if (fallback && Object.keys(fallback).length > 0) return fallback;
982
+ return {};
983
+ }
984
+ return out;
985
+ }
986
+
987
+ function applyCooldownsMutation(mutator) {
988
+ const { getDb, withTransaction } = require('./db');
989
+ let db;
990
+ try { db = getDb(); }
991
+ catch (e) { throw new Error(`small-state-store: SQLite unavailable (${e.message})`); }
992
+
993
+ return withTransaction(db, () => {
994
+ _resyncCooldownsIfDiverged(db);
995
+ const before = _readCooldownsFromSql(db);
996
+ const beforeSnap = JSON.parse(JSON.stringify(before));
997
+ const next = mutator(before);
998
+ const after = (next === undefined || next === null) ? before : next;
999
+ if (!after || typeof after !== 'object' || Array.isArray(after)) {
1000
+ return { wrote: false, result: beforeSnap };
1001
+ }
1002
+ const afterIds = new Set(Object.keys(after));
1003
+ const beforeIds = new Set(Object.keys(beforeSnap));
1004
+ let wrote = false;
1005
+ const now = Date.now();
1006
+ const upsert = db.prepare(`
1007
+ INSERT INTO cooldowns (key, data, updated_at)
1008
+ VALUES (?, ?, ?)
1009
+ ON CONFLICT(key) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at
1010
+ `);
1011
+ const del = db.prepare('DELETE FROM cooldowns WHERE key = ?');
1012
+ for (const id of afterIds) {
1013
+ if (!beforeIds.has(id) || JSON.stringify(beforeSnap[id]) !== JSON.stringify(after[id])) {
1014
+ upsert.run(id, JSON.stringify(after[id]), now);
1015
+ wrote = true;
1016
+ }
1017
+ }
1018
+ for (const id of beforeIds) {
1019
+ if (!afterIds.has(id)) { del.run(id); wrote = true; }
1020
+ }
1021
+ return { wrote, result: after };
1022
+ });
1023
+ }
1024
+
1025
+ function _mirrorCooldownsJson(filePath) {
1026
+ try {
1027
+ const shared = require('./shared');
1028
+ const { getDb } = require('./db');
1029
+ const obj = _readCooldownsFromSql(getDb());
1030
+ const target = filePath || _resolveFilePath('cooldowns.json');
1031
+ shared.safeWrite(target, obj);
1032
+ const h = _fileContentHash(target);
1033
+ if (h != null) _cooldownsHash = h;
1034
+ } catch { /* mirror best-effort */ }
1035
+ }
1036
+
1037
+ // ─── pending_rebases ───────────────────────────────────────────────────────
1038
+ // Shape: [ { prId, branch, projectName, mergedItemId, queuedAt, attempts, ... }, ... ]
1039
+ // SQL: row per array entry (rowid auto-increment preserves insertion order).
1040
+ // No stable natural key, so mutations are full clear+insert.
1041
+
1042
+ let _pendingRebasesHash = null;
1043
+
1044
+ function _hydratePendingRebases(db) {
1045
+ const fp = _resolveFilePath('pending-rebases.json');
1046
+ const raw = _readJson(fp) || [];
1047
+ db.prepare('DELETE FROM pending_rebases').run();
1048
+ if (!Array.isArray(raw)) return;
1049
+ const now = Date.now();
1050
+ const ins = db.prepare('INSERT INTO pending_rebases (data, updated_at) VALUES (?, ?)');
1051
+ for (const entry of raw) {
1052
+ if (!entry || typeof entry !== 'object') continue;
1053
+ ins.run(JSON.stringify(entry), now);
1054
+ }
1055
+ }
1056
+
1057
+ function _resyncPendingRebasesIfDiverged(db) {
1058
+ const fp = _resolveFilePath('pending-rebases.json');
1059
+ const currentHash = _fileContentHash(fp);
1060
+ if (currentHash == null) return;
1061
+ if (_pendingRebasesHash != null && currentHash === _pendingRebasesHash) return;
1062
+ if (_pendingRebasesHash == null) {
1063
+ const sqlHas = db.prepare('SELECT 1 FROM pending_rebases LIMIT 1').get();
1064
+ if (sqlHas) { _pendingRebasesHash = currentHash; return; }
1065
+ }
1066
+ _hydratePendingRebases(db);
1067
+ _pendingRebasesHash = currentHash;
1068
+ }
1069
+
1070
+ function _readPendingRebasesFromSql(db) {
1071
+ const rows = db.prepare('SELECT data FROM pending_rebases ORDER BY seq').all();
1072
+ const out = [];
1073
+ for (const row of rows) {
1074
+ try { out.push(JSON.parse(row.data)); }
1075
+ catch { /* skip malformed */ }
1076
+ }
1077
+ return out;
1078
+ }
1079
+
1080
+ function readPendingRebases() {
1081
+ const { getDb } = require('./db');
1082
+ const db = getDb();
1083
+ try { _resyncPendingRebasesIfDiverged(db); }
1084
+ catch { /* table missing */ }
1085
+ let out;
1086
+ try { out = _readPendingRebasesFromSql(db); }
1087
+ catch { return _readJson(_resolveFilePath('pending-rebases.json')) || []; }
1088
+ if (out.length === 0) {
1089
+ const fallback = _readJson(_resolveFilePath('pending-rebases.json'));
1090
+ if (Array.isArray(fallback) && fallback.length > 0) return fallback;
1091
+ return [];
1092
+ }
1093
+ return out;
1094
+ }
1095
+
1096
+ function applyPendingRebasesMutation(mutator) {
1097
+ const { getDb, withTransaction } = require('./db');
1098
+ let db;
1099
+ try { db = getDb(); }
1100
+ catch (e) { throw new Error(`small-state-store: SQLite unavailable (${e.message})`); }
1101
+
1102
+ return withTransaction(db, () => {
1103
+ _resyncPendingRebasesIfDiverged(db);
1104
+ const before = _readPendingRebasesFromSql(db);
1105
+ const beforeSnap = JSON.parse(JSON.stringify(before));
1106
+ const next = mutator(before);
1107
+ const after = (next === undefined || next === null) ? before : next;
1108
+ if (!Array.isArray(after)) {
1109
+ return { wrote: false, result: beforeSnap };
1110
+ }
1111
+ let wrote = false;
1112
+ if (JSON.stringify(before) !== JSON.stringify(after)) {
1113
+ const now = Date.now();
1114
+ db.prepare('DELETE FROM pending_rebases').run();
1115
+ const ins = db.prepare('INSERT INTO pending_rebases (data, updated_at) VALUES (?, ?)');
1116
+ for (const entry of after) {
1117
+ if (!entry || typeof entry !== 'object') continue;
1118
+ ins.run(JSON.stringify(entry), now);
1119
+ }
1120
+ wrote = true;
1121
+ }
1122
+ return { wrote, result: after };
1123
+ });
1124
+ }
1125
+
1126
+ function _mirrorPendingRebasesJson(filePath) {
1127
+ try {
1128
+ const shared = require('./shared');
1129
+ const { getDb } = require('./db');
1130
+ const arr = _readPendingRebasesFromSql(getDb());
1131
+ const target = filePath || _resolveFilePath('pending-rebases.json');
1132
+ shared.safeWrite(target, arr);
1133
+ const h = _fileContentHash(target);
1134
+ if (h != null) _pendingRebasesHash = h;
1135
+ } catch { /* mirror best-effort */ }
1136
+ }
1137
+
1138
+ // ─── cc_sessions ───────────────────────────────────────────────────────────
1139
+ // Shape: [ { id, sessionId, title, _promptHash, lastActiveAt, ... }, ... ]
1140
+ // SQL: row per id (each entry has a unique `id` tab key).
1141
+
1142
+ let _ccSessionsHash = null;
1143
+
1144
+ function _hydrateCcSessions(db) {
1145
+ const fp = _resolveFilePath('cc-sessions.json');
1146
+ const raw = _readJson(fp) || [];
1147
+ db.prepare('DELETE FROM cc_sessions').run();
1148
+ if (!Array.isArray(raw)) return;
1149
+ const now = Date.now();
1150
+ const ins = db.prepare('INSERT INTO cc_sessions (id, data, updated_at) VALUES (?, ?, ?) ON CONFLICT(id) DO NOTHING');
1151
+ for (const entry of raw) {
1152
+ if (!entry || typeof entry !== 'object' || !entry.id) continue;
1153
+ ins.run(String(entry.id), JSON.stringify(entry), now);
1154
+ }
1155
+ }
1156
+
1157
+ function _resyncCcSessionsIfDiverged(db) {
1158
+ const fp = _resolveFilePath('cc-sessions.json');
1159
+ const currentHash = _fileContentHash(fp);
1160
+ if (currentHash == null) return;
1161
+ if (_ccSessionsHash != null && currentHash === _ccSessionsHash) return;
1162
+ if (_ccSessionsHash == null) {
1163
+ const sqlHas = db.prepare('SELECT 1 FROM cc_sessions LIMIT 1').get();
1164
+ if (sqlHas) { _ccSessionsHash = currentHash; return; }
1165
+ }
1166
+ _hydrateCcSessions(db);
1167
+ _ccSessionsHash = currentHash;
1168
+ }
1169
+
1170
+ function _readCcSessionsFromSql(db) {
1171
+ const rows = db.prepare('SELECT data FROM cc_sessions ORDER BY updated_at, id').all();
1172
+ const out = [];
1173
+ for (const row of rows) {
1174
+ try { out.push(JSON.parse(row.data)); }
1175
+ catch { /* skip malformed */ }
1176
+ }
1177
+ return out;
1178
+ }
1179
+
1180
+ function readCcSessions() {
1181
+ const { getDb } = require('./db');
1182
+ const db = getDb();
1183
+ try { _resyncCcSessionsIfDiverged(db); }
1184
+ catch { /* table missing */ }
1185
+ let out;
1186
+ try { out = _readCcSessionsFromSql(db); }
1187
+ catch { return _readJson(_resolveFilePath('cc-sessions.json')) || []; }
1188
+ if (out.length === 0) {
1189
+ const fallback = _readJson(_resolveFilePath('cc-sessions.json'));
1190
+ if (Array.isArray(fallback) && fallback.length > 0) return fallback;
1191
+ return [];
1192
+ }
1193
+ return out;
1194
+ }
1195
+
1196
+ function applyCcSessionsMutation(mutator) {
1197
+ const { getDb, withTransaction } = require('./db');
1198
+ let db;
1199
+ try { db = getDb(); }
1200
+ catch (e) { throw new Error(`small-state-store: SQLite unavailable (${e.message})`); }
1201
+
1202
+ return withTransaction(db, () => {
1203
+ _resyncCcSessionsIfDiverged(db);
1204
+ const before = _readCcSessionsFromSql(db);
1205
+ const beforeSnap = JSON.parse(JSON.stringify(before));
1206
+ const next = mutator(before);
1207
+ const after = (next === undefined || next === null) ? before : next;
1208
+ if (!Array.isArray(after)) {
1209
+ return { wrote: false, result: beforeSnap };
1210
+ }
1211
+ const beforeById = new Map(beforeSnap.filter(e => e && e.id).map(e => [String(e.id), e]));
1212
+ const afterById = new Map(after.filter(e => e && e.id).map(e => [String(e.id), e]));
1213
+ let wrote = false;
1214
+ const now = Date.now();
1215
+ const upsert = db.prepare(`
1216
+ INSERT INTO cc_sessions (id, data, updated_at)
1217
+ VALUES (?, ?, ?)
1218
+ ON CONFLICT(id) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at
1219
+ `);
1220
+ const del = db.prepare('DELETE FROM cc_sessions WHERE id = ?');
1221
+ for (const [id, entry] of afterById) {
1222
+ const prior = beforeById.get(id);
1223
+ if (!prior || JSON.stringify(prior) !== JSON.stringify(entry)) {
1224
+ upsert.run(id, JSON.stringify(entry), now);
1225
+ wrote = true;
1226
+ }
1227
+ }
1228
+ for (const id of beforeById.keys()) {
1229
+ if (!afterById.has(id)) { del.run(id); wrote = true; }
1230
+ }
1231
+ return { wrote, result: after };
1232
+ });
1233
+ }
1234
+
1235
+ function _mirrorCcSessionsJson(filePath) {
1236
+ try {
1237
+ const shared = require('./shared');
1238
+ const { getDb } = require('./db');
1239
+ const arr = _readCcSessionsFromSql(getDb());
1240
+ const target = filePath || _resolveFilePath('cc-sessions.json');
1241
+ shared.safeWrite(target, arr);
1242
+ const h = _fileContentHash(target);
1243
+ if (h != null) _ccSessionsHash = h;
1244
+ } catch { /* mirror best-effort */ }
1245
+ }
1246
+
1247
+ // ─── doc_sessions ──────────────────────────────────────────────────────────
1248
+ // Shape: { [filePath]: { sessionId, lastActiveAt, turnCount, ... } }
1249
+ // SQL: row per filePath key.
1250
+
1251
+ let _docSessionsHash = null;
1252
+
1253
+ function _hydrateDocSessions(db) {
1254
+ const fp = _resolveFilePath('doc-sessions.json');
1255
+ const raw = _readJson(fp) || {};
1256
+ db.prepare('DELETE FROM doc_sessions').run();
1257
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return;
1258
+ const now = Date.now();
1259
+ const ins = db.prepare('INSERT INTO doc_sessions (key, data, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO NOTHING');
1260
+ for (const [key, value] of Object.entries(raw)) {
1261
+ ins.run(String(key), JSON.stringify(value), now);
1262
+ }
1263
+ }
1264
+
1265
+ function _resyncDocSessionsIfDiverged(db) {
1266
+ const fp = _resolveFilePath('doc-sessions.json');
1267
+ const currentHash = _fileContentHash(fp);
1268
+ if (currentHash == null) return;
1269
+ if (_docSessionsHash != null && currentHash === _docSessionsHash) return;
1270
+ if (_docSessionsHash == null) {
1271
+ const sqlHas = db.prepare('SELECT 1 FROM doc_sessions LIMIT 1').get();
1272
+ if (sqlHas) { _docSessionsHash = currentHash; return; }
1273
+ }
1274
+ _hydrateDocSessions(db);
1275
+ _docSessionsHash = currentHash;
1276
+ }
1277
+
1278
+ function _readDocSessionsFromSql(db) {
1279
+ const rows = db.prepare('SELECT key, data FROM doc_sessions').all();
1280
+ const out = {};
1281
+ for (const row of rows) {
1282
+ try { out[row.key] = JSON.parse(row.data); }
1283
+ catch { /* skip malformed */ }
1284
+ }
1285
+ return out;
1286
+ }
1287
+
1288
+ function readDocSessions() {
1289
+ const { getDb } = require('./db');
1290
+ const db = getDb();
1291
+ try { _resyncDocSessionsIfDiverged(db); }
1292
+ catch { /* table missing */ }
1293
+ let out;
1294
+ try { out = _readDocSessionsFromSql(db); }
1295
+ catch { return _readJson(_resolveFilePath('doc-sessions.json')) || {}; }
1296
+ if (Object.keys(out).length === 0) {
1297
+ const fallback = _readJson(_resolveFilePath('doc-sessions.json'));
1298
+ if (fallback && Object.keys(fallback).length > 0) return fallback;
1299
+ return {};
1300
+ }
1301
+ return out;
1302
+ }
1303
+
1304
+ function applyDocSessionsMutation(mutator) {
1305
+ const { getDb, withTransaction } = require('./db');
1306
+ let db;
1307
+ try { db = getDb(); }
1308
+ catch (e) { throw new Error(`small-state-store: SQLite unavailable (${e.message})`); }
1309
+
1310
+ return withTransaction(db, () => {
1311
+ _resyncDocSessionsIfDiverged(db);
1312
+ const before = _readDocSessionsFromSql(db);
1313
+ const beforeSnap = JSON.parse(JSON.stringify(before));
1314
+ const next = mutator(before);
1315
+ const after = (next === undefined || next === null) ? before : next;
1316
+ if (!after || typeof after !== 'object' || Array.isArray(after)) {
1317
+ return { wrote: false, result: beforeSnap };
1318
+ }
1319
+ const afterIds = new Set(Object.keys(after));
1320
+ const beforeIds = new Set(Object.keys(beforeSnap));
1321
+ let wrote = false;
1322
+ const now = Date.now();
1323
+ const upsert = db.prepare(`
1324
+ INSERT INTO doc_sessions (key, data, updated_at)
1325
+ VALUES (?, ?, ?)
1326
+ ON CONFLICT(key) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at
1327
+ `);
1328
+ const del = db.prepare('DELETE FROM doc_sessions WHERE key = ?');
1329
+ for (const id of afterIds) {
1330
+ if (!beforeIds.has(id) || JSON.stringify(beforeSnap[id]) !== JSON.stringify(after[id])) {
1331
+ upsert.run(id, JSON.stringify(after[id]), now);
1332
+ wrote = true;
1333
+ }
1334
+ }
1335
+ for (const id of beforeIds) {
1336
+ if (!afterIds.has(id)) { del.run(id); wrote = true; }
1337
+ }
1338
+ return { wrote, result: after };
1339
+ });
1340
+ }
1341
+
1342
+ function _mirrorDocSessionsJson(filePath) {
1343
+ try {
1344
+ const shared = require('./shared');
1345
+ const { getDb } = require('./db');
1346
+ const obj = _readDocSessionsFromSql(getDb());
1347
+ const target = filePath || _resolveFilePath('doc-sessions.json');
1348
+ shared.safeWrite(target, obj);
1349
+ const h = _fileContentHash(target);
1350
+ if (h != null) _docSessionsHash = h;
1351
+ } catch { /* mirror best-effort */ }
1352
+ }
1353
+
944
1354
  // ─── Test seam ─────────────────────────────────────────────────────────────
945
1355
 
946
1356
  function _resetAllForTest() {
@@ -954,6 +1364,10 @@ function _resetAllForTest() {
954
1364
  try { db.exec('DELETE FROM qa_runs'); } catch { /* migration not applied */ }
955
1365
  try { db.exec('DELETE FROM qa_sessions'); } catch { /* migration not applied */ }
956
1366
  try { db.exec('DELETE FROM pr_links'); } catch { /* migration not applied */ }
1367
+ try { db.exec('DELETE FROM cooldowns'); } catch { /* migration not applied */ }
1368
+ try { db.exec('DELETE FROM pending_rebases'); } catch { /* migration not applied */ }
1369
+ try { db.exec('DELETE FROM cc_sessions'); } catch { /* migration not applied */ }
1370
+ try { db.exec('DELETE FROM doc_sessions'); } catch { /* migration not applied */ }
957
1371
  } catch { /* not initialized */ }
958
1372
  _scheduleRunsHash = null;
959
1373
  _pipelineRunsHash = null;
@@ -962,6 +1376,10 @@ function _resetAllForTest() {
962
1376
  _qaRunsHash = null;
963
1377
  _qaSessionsHash = null;
964
1378
  _prLinksHash = null;
1379
+ _cooldownsHash = null;
1380
+ _pendingRebasesHash = null;
1381
+ _ccSessionsHash = null;
1382
+ _docSessionsHash = null;
965
1383
  }
966
1384
 
967
1385
  module.exports = {
@@ -993,6 +1411,22 @@ module.exports = {
993
1411
  readPrLinks,
994
1412
  applyPrLinksMutation,
995
1413
  _mirrorPrLinksJson,
1414
+ // cooldowns
1415
+ readCooldowns,
1416
+ applyCooldownsMutation,
1417
+ _mirrorCooldownsJson,
1418
+ // pending_rebases
1419
+ readPendingRebases,
1420
+ applyPendingRebasesMutation,
1421
+ _mirrorPendingRebasesJson,
1422
+ // cc_sessions
1423
+ readCcSessions,
1424
+ applyCcSessionsMutation,
1425
+ _mirrorCcSessionsJson,
1426
+ // doc_sessions
1427
+ readDocSessions,
1428
+ applyDocSessionsMutation,
1429
+ _mirrorDocSessionsJson,
996
1430
  // test seam
997
1431
  _resetAllForTest,
998
1432
  };
@@ -75,9 +75,7 @@ function _readJsonArrayFallback(scope) {
75
75
 
76
76
  function readWorkItemsForScope(scope) {
77
77
  const { getDb } = require('./db');
78
- let db;
79
- try { db = getDb(); }
80
- catch { return _readJsonArrayFallback(scope); }
78
+ const db = getDb();
81
79
 
82
80
  _resyncScopeIfJsonDiverged(db, scope);
83
81
 
@@ -88,10 +86,9 @@ function readWorkItemsForScope(scope) {
88
86
  `).all(scope);
89
87
 
90
88
  if (rows.length === 0) {
91
- // Same fallback shape as dispatch-store: SQL empty AND JSON has
92
- // content means a test seeded via fs.writeFileSync (legacy helper)
93
- // or a fresh install pre-migration. Returning the JSON keeps those
94
- // call sites working without touching every helper.
89
+ // SQL empty AND JSON has content means a test seeded via fs.writeFileSync
90
+ // (legacy helper) or a fresh install pre-migration. Returning the JSON
91
+ // keeps those call sites working without touching every helper.
95
92
  const fallback = _readJsonArrayFallback(scope);
96
93
  if (fallback.length > 0) return fallback;
97
94
  return [];
@@ -161,29 +158,9 @@ function _enumerateJsonScopes() {
161
158
  // Read all rows across all scopes — used by queries.getWorkItems which
162
159
  // needs to surface central + every project's items in a single shot,
163
160
  // tagged with their source scope.
164
- //
165
- // Issue #3035: SQL-unavailable installs (Node 22.x without
166
- // --experimental-sqlite, prior to v0.1.2113) returned [] from this path
167
- // because `getDb()` throws and the legacy `return null` signal was
168
- // swallowed by `queries.getWorkItems`'s `|| []`. Mirror the per-scope
169
- // reader's JSON-fallback shape so the aggregate API stays useful on
170
- // Node versions that don't have node:sqlite enabled.
171
161
  function readAllWorkItems() {
172
162
  const { getDb } = require('./db');
173
- let db;
174
- try { db = getDb(); }
175
- catch {
176
- // SQLite unavailable — read every JSON scope on disk directly.
177
- const out = [];
178
- for (const scope of _enumerateJsonScopes()) {
179
- for (const wi of _readJsonArrayFallback(scope)) {
180
- if (!wi || typeof wi !== 'object') continue;
181
- wi._source = scope;
182
- out.push(wi);
183
- }
184
- }
185
- return out;
186
- }
163
+ const db = getDb();
187
164
 
188
165
  // Pick up any external JSON edits for every scope SQL knows about.
189
166
  // Also resync 'central' explicitly so first-time reads on a JSON-only
@@ -326,11 +303,7 @@ function _hydrateScopeFromJson(db, scope) {
326
303
 
327
304
  function applyWorkItemsMutation(scope, mutator) {
328
305
  const { getDb, withTransaction } = require('./db');
329
- let db;
330
- try { db = getDb(); }
331
- catch (e) {
332
- throw new Error(`engine/work-items-store: SQLite unavailable (${e.message}); cannot mutate work_items`);
333
- }
306
+ const db = getDb();
334
307
 
335
308
  return withTransaction(db, () => {
336
309
  // Re-hydrate SQL from JSON if the file was touched outside the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2115",
3
+ "version": "0.1.2117",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"