@open-code-review/cli 2.2.1 → 2.4.0

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.
Files changed (64) hide show
  1. package/README.md +9 -0
  2. package/dist/dashboard/client/assets/{_basePickBy-BAlGnwHG.js → _basePickBy-CyrHyeyN.js} +1 -1
  3. package/dist/dashboard/client/assets/{_baseUniq-CoauyOeL.js → _baseUniq-Bg7NJSGS.js} +1 -1
  4. package/dist/dashboard/client/assets/{arc-DtS0aHfP.js → arc-zDGAKMur.js} +1 -1
  5. package/dist/dashboard/client/assets/{architectureDiagram-VXUJARFQ-CnWmtRTh.js → architectureDiagram-VXUJARFQ-BxlGxm0Q.js} +1 -1
  6. package/dist/dashboard/client/assets/{blockDiagram-VD42YOAC-DgPp4oGV.js → blockDiagram-VD42YOAC-BskTNyX5.js} +1 -1
  7. package/dist/dashboard/client/assets/{c4Diagram-YG6GDRKO--LV4qQaE.js → c4Diagram-YG6GDRKO-Dr9QQ-dn.js} +1 -1
  8. package/dist/dashboard/client/assets/channel-BUnm_-UQ.js +1 -0
  9. package/dist/dashboard/client/assets/{chunk-4BX2VUAB-BRglpc7Z.js → chunk-4BX2VUAB-xq9xoCTv.js} +1 -1
  10. package/dist/dashboard/client/assets/{chunk-55IACEB6-Bgx06_CV.js → chunk-55IACEB6-DYdXYVh5.js} +1 -1
  11. package/dist/dashboard/client/assets/{chunk-B4BG7PRW-D6HN3Yiy.js → chunk-B4BG7PRW-BGAyFRFS.js} +1 -1
  12. package/dist/dashboard/client/assets/{chunk-DI55MBZ5-NH9EgN9T.js → chunk-DI55MBZ5-C5ul9stk.js} +1 -1
  13. package/dist/dashboard/client/assets/{chunk-FMBD7UC4-xriO6WNP.js → chunk-FMBD7UC4-BSaPo2xa.js} +1 -1
  14. package/dist/dashboard/client/assets/{chunk-QN33PNHL-CV1h6_Zl.js → chunk-QN33PNHL-CyzabUv0.js} +1 -1
  15. package/dist/dashboard/client/assets/{chunk-QZHKN3VN-CV4VzxNq.js → chunk-QZHKN3VN-CceRbxt_.js} +1 -1
  16. package/dist/dashboard/client/assets/{chunk-TZMSLE5B-isdklocW.js → chunk-TZMSLE5B-Bjg9IoOQ.js} +1 -1
  17. package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-D_fkmNvU.js +1 -0
  18. package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-D_fkmNvU.js +1 -0
  19. package/dist/dashboard/client/assets/clone-DTyrNOLZ.js +1 -0
  20. package/dist/dashboard/client/assets/{cose-bilkent-S5V4N54A-CCzlFSJf.js → cose-bilkent-S5V4N54A-DEdXBrCt.js} +1 -1
  21. package/dist/dashboard/client/assets/{dagre-6UL2VRFP-DVN3PkjZ.js → dagre-6UL2VRFP-DRdIiP58.js} +1 -1
  22. package/dist/dashboard/client/assets/{diagram-PSM6KHXK-SzJVoSsb.js → diagram-PSM6KHXK-Bo7Q2VlK.js} +1 -1
  23. package/dist/dashboard/client/assets/{diagram-QEK2KX5R-CgGn7ts-.js → diagram-QEK2KX5R-2Fmc2o5x.js} +1 -1
  24. package/dist/dashboard/client/assets/{diagram-S2PKOQOG-Bz1ukSx8.js → diagram-S2PKOQOG-5WE8f0p7.js} +1 -1
  25. package/dist/dashboard/client/assets/{erDiagram-Q2GNP2WA-CpstUTMZ.js → erDiagram-Q2GNP2WA-DD-iXWd_.js} +1 -1
  26. package/dist/dashboard/client/assets/{flowDiagram-NV44I4VS-aYVydGhp.js → flowDiagram-NV44I4VS-CCWo8Ue9.js} +1 -1
  27. package/dist/dashboard/client/assets/{ganttDiagram-JELNMOA3-Cb2DUSRk.js → ganttDiagram-JELNMOA3-CNY4d5UK.js} +1 -1
  28. package/dist/dashboard/client/assets/{gitGraphDiagram-V2S2FVAM-BUOnwA2w.js → gitGraphDiagram-V2S2FVAM-Dq5SBEJJ.js} +1 -1
  29. package/dist/dashboard/client/assets/{graph-4X5ddhLp.js → graph-BTt9lokK.js} +1 -1
  30. package/dist/dashboard/client/assets/{index-CKWqYAfu.js → index-B0k81q2b.js} +138 -138
  31. package/dist/dashboard/client/assets/index-Czwdh6UA.css +1 -0
  32. package/dist/dashboard/client/assets/{infoDiagram-HS3SLOUP-BlMqcrwm.js → infoDiagram-HS3SLOUP-AnKZja-G.js} +1 -1
  33. package/dist/dashboard/client/assets/{journeyDiagram-XKPGCS4Q-DF2ew7ju.js → journeyDiagram-XKPGCS4Q-nC-_WjPN.js} +1 -1
  34. package/dist/dashboard/client/assets/{kanban-definition-3W4ZIXB7-BKQMx0-n.js → kanban-definition-3W4ZIXB7-BEY73sWU.js} +1 -1
  35. package/dist/dashboard/client/assets/{layout-DNcn2g9w.js → layout-D4DfNpzH.js} +1 -1
  36. package/dist/dashboard/client/assets/{linear-Bqy9gvqb.js → linear-ZpGvKjeP.js} +1 -1
  37. package/dist/dashboard/client/assets/{mermaid-renderer-dJ71wgld.js → mermaid-renderer-BCDxmS9g.js} +4 -4
  38. package/dist/dashboard/client/assets/{mindmap-definition-VGOIOE7T-BARc8sqJ.js → mindmap-definition-VGOIOE7T-MzAaKESA.js} +1 -1
  39. package/dist/dashboard/client/assets/{pieDiagram-ADFJNKIX-CULlNZTd.js → pieDiagram-ADFJNKIX-B_X1kySF.js} +1 -1
  40. package/dist/dashboard/client/assets/{quadrantDiagram-AYHSOK5B-BJEZPVe9.js → quadrantDiagram-AYHSOK5B-CMoIEMLN.js} +1 -1
  41. package/dist/dashboard/client/assets/{requirementDiagram-UZGBJVZJ-BhMsmUIs.js → requirementDiagram-UZGBJVZJ-v4CRsn1w.js} +1 -1
  42. package/dist/dashboard/client/assets/{sankeyDiagram-TZEHDZUN-BYbNgogG.js → sankeyDiagram-TZEHDZUN-CPcyN8Jj.js} +1 -1
  43. package/dist/dashboard/client/assets/{sequenceDiagram-WL72ISMW-MoM_NwWk.js → sequenceDiagram-WL72ISMW-CTg0Vx1H.js} +1 -1
  44. package/dist/dashboard/client/assets/{stateDiagram-FKZM4ZOC-ditrlbM3.js → stateDiagram-FKZM4ZOC-BMWBN6Nq.js} +1 -1
  45. package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-C9Jk1xd0.js +1 -0
  46. package/dist/dashboard/client/assets/{timeline-definition-IT6M3QCI-DOAJyjuz.js → timeline-definition-IT6M3QCI-B8xFcSGb.js} +1 -1
  47. package/dist/dashboard/client/assets/{treemap-GDKQZRPO-BBJkjnJl.js → treemap-GDKQZRPO-HQQuGl9w.js} +1 -1
  48. package/dist/dashboard/client/assets/{xychartDiagram-PRI3JC2R-CPW4s5vm.js → xychartDiagram-PRI3JC2R-Drz0SW3I.js} +1 -1
  49. package/dist/dashboard/client/index.html +2 -2
  50. package/dist/dashboard/server.js +926 -461
  51. package/dist/index.js +1344 -323
  52. package/package.json +5 -38
  53. package/dist/dashboard/client/assets/channel-BU2129fl.js +0 -1
  54. package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-CVftFGiR.js +0 -1
  55. package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-CVftFGiR.js +0 -1
  56. package/dist/dashboard/client/assets/clone-DC6LEEC5.js +0 -1
  57. package/dist/dashboard/client/assets/index-CzxeSSaQ.css +0 -1
  58. package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-SqoG2LCn.js +0 -1
  59. package/dist/lib/db/index.js +0 -2177
  60. package/dist/lib/models.js +0 -160
  61. package/dist/lib/runtime-config.js +0 -55
  62. package/dist/lib/state/index.js +0 -2196
  63. package/dist/lib/team-config.js +0 -175
  64. package/dist/lib/vendor-resume.js +0 -31
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  mod
40
40
  ));
41
41
 
42
- // src/lib/runtime-checks.ts
42
+ // ../shared/persistence/src/runtime-checks.ts
43
43
  function isSupportedNode(version) {
44
44
  const [major = 0, minor = 0] = version.split(".").map((n) => Number.parseInt(n, 10) || 0);
45
45
  return major > NODE_FLOOR.major || major === NODE_FLOOR.major && minor >= NODE_FLOOR.minor;
@@ -57,7 +57,7 @@ function isSuppressibleSqliteWarning(warning) {
57
57
  }
58
58
  var NODE_FLOOR;
59
59
  var init_runtime_checks = __esm({
60
- "src/lib/runtime-checks.ts"() {
60
+ "../shared/persistence/src/runtime-checks.ts"() {
61
61
  "use strict";
62
62
  NODE_FLOOR = { major: 22, minor: 5 };
63
63
  }
@@ -16017,35 +16017,688 @@ var require_emoji_regex2 = __commonJS({
16017
16017
  }
16018
16018
  });
16019
16019
 
16020
- // ../shared/platform/src/index.ts
16021
- import { pathToFileURL } from "node:url";
16022
- import {
16023
- execFile,
16024
- execFileSync,
16025
- spawn as spawn2
16026
- } from "node:child_process";
16027
- import { promisify } from "node:util";
16028
- async function importModule(absolutePath) {
16029
- return import(pathToFileURL(absolutePath).href);
16030
- }
16020
+ // ../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js
16021
+ var require_windows = __commonJS({
16022
+ "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports, module) {
16023
+ "use strict";
16024
+ module.exports = isexe;
16025
+ isexe.sync = sync;
16026
+ var fs = __require("fs");
16027
+ function checkPathExt(path2, options) {
16028
+ var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
16029
+ if (!pathext) {
16030
+ return true;
16031
+ }
16032
+ pathext = pathext.split(";");
16033
+ if (pathext.indexOf("") !== -1) {
16034
+ return true;
16035
+ }
16036
+ for (var i = 0; i < pathext.length; i++) {
16037
+ var p = pathext[i].toLowerCase();
16038
+ if (p && path2.substr(-p.length).toLowerCase() === p) {
16039
+ return true;
16040
+ }
16041
+ }
16042
+ return false;
16043
+ }
16044
+ function checkStat(stat4, path2, options) {
16045
+ if (!stat4.isSymbolicLink() && !stat4.isFile()) {
16046
+ return false;
16047
+ }
16048
+ return checkPathExt(path2, options);
16049
+ }
16050
+ function isexe(path2, options, cb) {
16051
+ fs.stat(path2, function(er, stat4) {
16052
+ cb(er, er ? false : checkStat(stat4, path2, options));
16053
+ });
16054
+ }
16055
+ function sync(path2, options) {
16056
+ return checkStat(fs.statSync(path2), path2, options);
16057
+ }
16058
+ }
16059
+ });
16060
+
16061
+ // ../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js
16062
+ var require_mode = __commonJS({
16063
+ "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports, module) {
16064
+ "use strict";
16065
+ module.exports = isexe;
16066
+ isexe.sync = sync;
16067
+ var fs = __require("fs");
16068
+ function isexe(path2, options, cb) {
16069
+ fs.stat(path2, function(er, stat4) {
16070
+ cb(er, er ? false : checkStat(stat4, options));
16071
+ });
16072
+ }
16073
+ function sync(path2, options) {
16074
+ return checkStat(fs.statSync(path2), options);
16075
+ }
16076
+ function checkStat(stat4, options) {
16077
+ return stat4.isFile() && checkMode(stat4, options);
16078
+ }
16079
+ function checkMode(stat4, options) {
16080
+ var mod = stat4.mode;
16081
+ var uid = stat4.uid;
16082
+ var gid = stat4.gid;
16083
+ var myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid();
16084
+ var myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid();
16085
+ var u = parseInt("100", 8);
16086
+ var g = parseInt("010", 8);
16087
+ var o = parseInt("001", 8);
16088
+ var ug = u | g;
16089
+ var ret = mod & o || mod & g && gid === myGid || mod & u && uid === myUid || mod & ug && myUid === 0;
16090
+ return ret;
16091
+ }
16092
+ }
16093
+ });
16094
+
16095
+ // ../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js
16096
+ var require_isexe = __commonJS({
16097
+ "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports, module) {
16098
+ "use strict";
16099
+ var fs = __require("fs");
16100
+ var core;
16101
+ if (process.platform === "win32" || global.TESTING_WINDOWS) {
16102
+ core = require_windows();
16103
+ } else {
16104
+ core = require_mode();
16105
+ }
16106
+ module.exports = isexe;
16107
+ isexe.sync = sync;
16108
+ function isexe(path2, options, cb) {
16109
+ if (typeof options === "function") {
16110
+ cb = options;
16111
+ options = {};
16112
+ }
16113
+ if (!cb) {
16114
+ if (typeof Promise !== "function") {
16115
+ throw new TypeError("callback not provided");
16116
+ }
16117
+ return new Promise(function(resolve3, reject) {
16118
+ isexe(path2, options || {}, function(er, is) {
16119
+ if (er) {
16120
+ reject(er);
16121
+ } else {
16122
+ resolve3(is);
16123
+ }
16124
+ });
16125
+ });
16126
+ }
16127
+ core(path2, options || {}, function(er, is) {
16128
+ if (er) {
16129
+ if (er.code === "EACCES" || options && options.ignoreErrors) {
16130
+ er = null;
16131
+ is = false;
16132
+ }
16133
+ }
16134
+ cb(er, is);
16135
+ });
16136
+ }
16137
+ function sync(path2, options) {
16138
+ try {
16139
+ return core.sync(path2, options || {});
16140
+ } catch (er) {
16141
+ if (options && options.ignoreErrors || er.code === "EACCES") {
16142
+ return false;
16143
+ } else {
16144
+ throw er;
16145
+ }
16146
+ }
16147
+ }
16148
+ }
16149
+ });
16150
+
16151
+ // ../../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js
16152
+ var require_which = __commonJS({
16153
+ "../../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports, module) {
16154
+ "use strict";
16155
+ var isWindows5 = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
16156
+ var path2 = __require("path");
16157
+ var COLON = isWindows5 ? ";" : ":";
16158
+ var isexe = require_isexe();
16159
+ var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
16160
+ var getPathInfo = (cmd, opt) => {
16161
+ const colon = opt.colon || COLON;
16162
+ const pathEnv = cmd.match(/\//) || isWindows5 && cmd.match(/\\/) ? [""] : [
16163
+ // windows always checks the cwd first
16164
+ ...isWindows5 ? [process.cwd()] : [],
16165
+ ...(opt.path || process.env.PATH || /* istanbul ignore next: very unusual */
16166
+ "").split(colon)
16167
+ ];
16168
+ const pathExtExe = isWindows5 ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : "";
16169
+ const pathExt = isWindows5 ? pathExtExe.split(colon) : [""];
16170
+ if (isWindows5) {
16171
+ if (cmd.indexOf(".") !== -1 && pathExt[0] !== "")
16172
+ pathExt.unshift("");
16173
+ }
16174
+ return {
16175
+ pathEnv,
16176
+ pathExt,
16177
+ pathExtExe
16178
+ };
16179
+ };
16180
+ var which = (cmd, opt, cb) => {
16181
+ if (typeof opt === "function") {
16182
+ cb = opt;
16183
+ opt = {};
16184
+ }
16185
+ if (!opt)
16186
+ opt = {};
16187
+ const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
16188
+ const found = [];
16189
+ const step = (i) => new Promise((resolve3, reject) => {
16190
+ if (i === pathEnv.length)
16191
+ return opt.all && found.length ? resolve3(found) : reject(getNotFoundError(cmd));
16192
+ const ppRaw = pathEnv[i];
16193
+ const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
16194
+ const pCmd = path2.join(pathPart, cmd);
16195
+ const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
16196
+ resolve3(subStep(p, i, 0));
16197
+ });
16198
+ const subStep = (p, i, ii) => new Promise((resolve3, reject) => {
16199
+ if (ii === pathExt.length)
16200
+ return resolve3(step(i + 1));
16201
+ const ext = pathExt[ii];
16202
+ isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
16203
+ if (!er && is) {
16204
+ if (opt.all)
16205
+ found.push(p + ext);
16206
+ else
16207
+ return resolve3(p + ext);
16208
+ }
16209
+ return resolve3(subStep(p, i, ii + 1));
16210
+ });
16211
+ });
16212
+ return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
16213
+ };
16214
+ var whichSync = (cmd, opt) => {
16215
+ opt = opt || {};
16216
+ const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
16217
+ const found = [];
16218
+ for (let i = 0; i < pathEnv.length; i++) {
16219
+ const ppRaw = pathEnv[i];
16220
+ const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
16221
+ const pCmd = path2.join(pathPart, cmd);
16222
+ const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
16223
+ for (let j = 0; j < pathExt.length; j++) {
16224
+ const cur = p + pathExt[j];
16225
+ try {
16226
+ const is = isexe.sync(cur, { pathExt: pathExtExe });
16227
+ if (is) {
16228
+ if (opt.all)
16229
+ found.push(cur);
16230
+ else
16231
+ return cur;
16232
+ }
16233
+ } catch (ex) {
16234
+ }
16235
+ }
16236
+ }
16237
+ if (opt.all && found.length)
16238
+ return found;
16239
+ if (opt.nothrow)
16240
+ return null;
16241
+ throw getNotFoundError(cmd);
16242
+ };
16243
+ module.exports = which;
16244
+ which.sync = whichSync;
16245
+ }
16246
+ });
16247
+
16248
+ // ../../node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js
16249
+ var require_path_key = __commonJS({
16250
+ "../../node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js"(exports, module) {
16251
+ "use strict";
16252
+ var pathKey = (options = {}) => {
16253
+ const environment = options.env || process.env;
16254
+ const platform2 = options.platform || process.platform;
16255
+ if (platform2 !== "win32") {
16256
+ return "PATH";
16257
+ }
16258
+ return Object.keys(environment).reverse().find((key) => key.toUpperCase() === "PATH") || "Path";
16259
+ };
16260
+ module.exports = pathKey;
16261
+ module.exports.default = pathKey;
16262
+ }
16263
+ });
16264
+
16265
+ // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js
16266
+ var require_resolveCommand = __commonJS({
16267
+ "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
16268
+ "use strict";
16269
+ var path2 = __require("path");
16270
+ var which = require_which();
16271
+ var getPathKey = require_path_key();
16272
+ function resolveCommandAttempt(parsed, withoutPathExt) {
16273
+ const env2 = parsed.options.env || process.env;
16274
+ const cwd = process.cwd();
16275
+ const hasCustomCwd = parsed.options.cwd != null;
16276
+ const shouldSwitchCwd = hasCustomCwd && process.chdir !== void 0 && !process.chdir.disabled;
16277
+ if (shouldSwitchCwd) {
16278
+ try {
16279
+ process.chdir(parsed.options.cwd);
16280
+ } catch (err) {
16281
+ }
16282
+ }
16283
+ let resolved;
16284
+ try {
16285
+ resolved = which.sync(parsed.command, {
16286
+ path: env2[getPathKey({ env: env2 })],
16287
+ pathExt: withoutPathExt ? path2.delimiter : void 0
16288
+ });
16289
+ } catch (e) {
16290
+ } finally {
16291
+ if (shouldSwitchCwd) {
16292
+ process.chdir(cwd);
16293
+ }
16294
+ }
16295
+ if (resolved) {
16296
+ resolved = path2.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
16297
+ }
16298
+ return resolved;
16299
+ }
16300
+ function resolveCommand(parsed) {
16301
+ return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
16302
+ }
16303
+ module.exports = resolveCommand;
16304
+ }
16305
+ });
16306
+
16307
+ // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.js
16308
+ var require_escape = __commonJS({
16309
+ "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.js"(exports, module) {
16310
+ "use strict";
16311
+ var metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
16312
+ function escapeCommand(arg) {
16313
+ arg = arg.replace(metaCharsRegExp, "^$1");
16314
+ return arg;
16315
+ }
16316
+ function escapeArgument(arg, doubleEscapeMetaChars) {
16317
+ arg = `${arg}`;
16318
+ arg = arg.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
16319
+ arg = arg.replace(/(?=(\\+?)?)\1$/, "$1$1");
16320
+ arg = `"${arg}"`;
16321
+ arg = arg.replace(metaCharsRegExp, "^$1");
16322
+ if (doubleEscapeMetaChars) {
16323
+ arg = arg.replace(metaCharsRegExp, "^$1");
16324
+ }
16325
+ return arg;
16326
+ }
16327
+ module.exports.command = escapeCommand;
16328
+ module.exports.argument = escapeArgument;
16329
+ }
16330
+ });
16331
+
16332
+ // ../../node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js
16333
+ var require_shebang_regex = __commonJS({
16334
+ "../../node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js"(exports, module) {
16335
+ "use strict";
16336
+ module.exports = /^#!(.*)/;
16337
+ }
16338
+ });
16339
+
16340
+ // ../../node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js
16341
+ var require_shebang_command = __commonJS({
16342
+ "../../node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js"(exports, module) {
16343
+ "use strict";
16344
+ var shebangRegex = require_shebang_regex();
16345
+ module.exports = (string = "") => {
16346
+ const match = string.match(shebangRegex);
16347
+ if (!match) {
16348
+ return null;
16349
+ }
16350
+ const [path2, argument] = match[0].replace(/#! ?/, "").split(" ");
16351
+ const binary = path2.split("/").pop();
16352
+ if (binary === "env") {
16353
+ return argument;
16354
+ }
16355
+ return argument ? `${binary} ${argument}` : binary;
16356
+ };
16357
+ }
16358
+ });
16359
+
16360
+ // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
16361
+ var require_readShebang = __commonJS({
16362
+ "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js"(exports, module) {
16363
+ "use strict";
16364
+ var fs = __require("fs");
16365
+ var shebangCommand = require_shebang_command();
16366
+ function readShebang(command) {
16367
+ const size = 150;
16368
+ const buffer = Buffer.alloc(size);
16369
+ let fd;
16370
+ try {
16371
+ fd = fs.openSync(command, "r");
16372
+ fs.readSync(fd, buffer, 0, size, 0);
16373
+ fs.closeSync(fd);
16374
+ } catch (e) {
16375
+ }
16376
+ return shebangCommand(buffer.toString());
16377
+ }
16378
+ module.exports = readShebang;
16379
+ }
16380
+ });
16381
+
16382
+ // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js
16383
+ var require_parse = __commonJS({
16384
+ "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js"(exports, module) {
16385
+ "use strict";
16386
+ var path2 = __require("path");
16387
+ var resolveCommand = require_resolveCommand();
16388
+ var escape = require_escape();
16389
+ var readShebang = require_readShebang();
16390
+ var isWin = process.platform === "win32";
16391
+ var isExecutableRegExp = /\.(?:com|exe)$/i;
16392
+ var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
16393
+ function detectShebang(parsed) {
16394
+ parsed.file = resolveCommand(parsed);
16395
+ const shebang = parsed.file && readShebang(parsed.file);
16396
+ if (shebang) {
16397
+ parsed.args.unshift(parsed.file);
16398
+ parsed.command = shebang;
16399
+ return resolveCommand(parsed);
16400
+ }
16401
+ return parsed.file;
16402
+ }
16403
+ function parseNonShell(parsed) {
16404
+ if (!isWin) {
16405
+ return parsed;
16406
+ }
16407
+ const commandFile = detectShebang(parsed);
16408
+ const needsShell = !isExecutableRegExp.test(commandFile);
16409
+ if (parsed.options.forceShell || needsShell) {
16410
+ const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
16411
+ parsed.command = path2.normalize(parsed.command);
16412
+ parsed.command = escape.command(parsed.command);
16413
+ parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
16414
+ const shellCommand = [parsed.command].concat(parsed.args).join(" ");
16415
+ parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`];
16416
+ parsed.command = process.env.comspec || "cmd.exe";
16417
+ parsed.options.windowsVerbatimArguments = true;
16418
+ }
16419
+ return parsed;
16420
+ }
16421
+ function parse(command, args, options) {
16422
+ if (args && !Array.isArray(args)) {
16423
+ options = args;
16424
+ args = null;
16425
+ }
16426
+ args = args ? args.slice(0) : [];
16427
+ options = Object.assign({}, options);
16428
+ const parsed = {
16429
+ command,
16430
+ args,
16431
+ options,
16432
+ file: void 0,
16433
+ original: {
16434
+ command,
16435
+ args
16436
+ }
16437
+ };
16438
+ return options.shell ? parsed : parseNonShell(parsed);
16439
+ }
16440
+ module.exports = parse;
16441
+ }
16442
+ });
16443
+
16444
+ // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.js
16445
+ var require_enoent = __commonJS({
16446
+ "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.js"(exports, module) {
16447
+ "use strict";
16448
+ var isWin = process.platform === "win32";
16449
+ function notFoundError(original, syscall) {
16450
+ return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
16451
+ code: "ENOENT",
16452
+ errno: "ENOENT",
16453
+ syscall: `${syscall} ${original.command}`,
16454
+ path: original.command,
16455
+ spawnargs: original.args
16456
+ });
16457
+ }
16458
+ function hookChildProcess(cp, parsed) {
16459
+ if (!isWin) {
16460
+ return;
16461
+ }
16462
+ const originalEmit = cp.emit;
16463
+ cp.emit = function(name, arg1) {
16464
+ if (name === "exit") {
16465
+ const err = verifyENOENT(arg1, parsed);
16466
+ if (err) {
16467
+ return originalEmit.call(cp, "error", err);
16468
+ }
16469
+ }
16470
+ return originalEmit.apply(cp, arguments);
16471
+ };
16472
+ }
16473
+ function verifyENOENT(status, parsed) {
16474
+ if (isWin && status === 1 && !parsed.file) {
16475
+ return notFoundError(parsed.original, "spawn");
16476
+ }
16477
+ return null;
16478
+ }
16479
+ function verifyENOENTSync(status, parsed) {
16480
+ if (isWin && status === 1 && !parsed.file) {
16481
+ return notFoundError(parsed.original, "spawnSync");
16482
+ }
16483
+ return null;
16484
+ }
16485
+ module.exports = {
16486
+ hookChildProcess,
16487
+ verifyENOENT,
16488
+ verifyENOENTSync,
16489
+ notFoundError
16490
+ };
16491
+ }
16492
+ });
16493
+
16494
+ // ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.js
16495
+ var require_cross_spawn = __commonJS({
16496
+ "../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.js"(exports, module) {
16497
+ "use strict";
16498
+ var cp = __require("child_process");
16499
+ var parse = require_parse();
16500
+ var enoent = require_enoent();
16501
+ function spawn2(command, args, options) {
16502
+ const parsed = parse(command, args, options);
16503
+ const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
16504
+ enoent.hookChildProcess(spawned, parsed);
16505
+ return spawned;
16506
+ }
16507
+ function spawnSync2(command, args, options) {
16508
+ const parsed = parse(command, args, options);
16509
+ const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
16510
+ result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
16511
+ return result;
16512
+ }
16513
+ module.exports = spawn2;
16514
+ module.exports.spawn = spawn2;
16515
+ module.exports.sync = spawnSync2;
16516
+ module.exports._parse = parse;
16517
+ module.exports._enoent = enoent;
16518
+ }
16519
+ });
16520
+
16521
+ // ../shared/platform/src/spawn.ts
16031
16522
  function execBinary(binary, args, opts) {
16032
- return execFileSync(binary, args, {
16033
- ...opts,
16034
- shell: isWindows
16523
+ const result = import_cross_spawn.default.sync(binary, args, {
16524
+ maxBuffer: DEFAULT_MAX_BUFFER,
16525
+ ...opts
16035
16526
  });
16527
+ if (result.error) throw result.error;
16528
+ if (result.status !== 0) {
16529
+ throw Object.assign(
16530
+ new Error(
16531
+ `Command failed: ${binary} ${args.join(" ")}
16532
+ ${String(result.stderr ?? "")}`
16533
+ ),
16534
+ {
16535
+ status: result.status,
16536
+ code: result.status,
16537
+ signal: result.signal,
16538
+ stdout: result.stdout,
16539
+ stderr: result.stderr,
16540
+ pid: result.pid
16541
+ }
16542
+ );
16543
+ }
16544
+ return result.stdout;
16036
16545
  }
16037
16546
  async function execBinaryAsync(binary, args, opts) {
16038
- return execFilePromise(binary, args, {
16547
+ return new Promise((resolvePromise, rejectPromise) => {
16548
+ const child = (0, import_cross_spawn.default)(binary, args, {
16549
+ cwd: opts.cwd,
16550
+ env: opts.env,
16551
+ windowsHide: true
16552
+ });
16553
+ const maxBuffer = opts.maxBuffer ?? DEFAULT_MAX_BUFFER;
16554
+ let stdout = "";
16555
+ let stderr = "";
16556
+ let killed = false;
16557
+ let settled = false;
16558
+ const settle = (fn) => {
16559
+ if (settled) return;
16560
+ settled = true;
16561
+ if (timer) clearTimeout(timer);
16562
+ fn();
16563
+ };
16564
+ const overflow = () => {
16565
+ killed = true;
16566
+ child.kill();
16567
+ };
16568
+ child.stdout?.setEncoding(opts.encoding);
16569
+ child.stderr?.setEncoding(opts.encoding);
16570
+ child.stdout?.on("data", (chunk) => {
16571
+ stdout += chunk;
16572
+ if (stdout.length > maxBuffer) overflow();
16573
+ });
16574
+ child.stderr?.on("data", (chunk) => {
16575
+ stderr += chunk;
16576
+ if (stderr.length > maxBuffer) overflow();
16577
+ });
16578
+ const timer = opts.timeout ? setTimeout(() => {
16579
+ killed = true;
16580
+ child.kill();
16581
+ }, opts.timeout) : void 0;
16582
+ child.on("error", (err) => {
16583
+ settle(
16584
+ () => rejectPromise(Object.assign(err, { stdout, stderr, killed }))
16585
+ );
16586
+ });
16587
+ child.on("close", (code, signal) => {
16588
+ settle(() => {
16589
+ if (code === 0 && !killed) {
16590
+ resolvePromise({ stdout, stderr });
16591
+ return;
16592
+ }
16593
+ rejectPromise(
16594
+ Object.assign(
16595
+ new Error(
16596
+ `Command failed: ${binary} ${args.join(" ")}
16597
+ ${stderr}`
16598
+ ),
16599
+ {
16600
+ code: code ?? void 0,
16601
+ signal,
16602
+ stdout,
16603
+ stderr,
16604
+ killed
16605
+ }
16606
+ )
16607
+ );
16608
+ });
16609
+ });
16610
+ });
16611
+ }
16612
+ function spawnBinary(binary, args, opts) {
16613
+ return (0, import_cross_spawn.default)(binary, args, {
16039
16614
  ...opts,
16040
- shell: isWindows
16615
+ windowsHide: true
16041
16616
  });
16042
16617
  }
16618
+ var import_cross_spawn, DEFAULT_MAX_BUFFER;
16619
+ var init_spawn = __esm({
16620
+ "../shared/platform/src/spawn.ts"() {
16621
+ "use strict";
16622
+ import_cross_spawn = __toESM(require_cross_spawn(), 1);
16623
+ DEFAULT_MAX_BUFFER = 1024 * 1024;
16624
+ }
16625
+ });
16626
+
16627
+ // ../shared/platform/src/verdict.ts
16628
+ function isCanonicalVerdict(v) {
16629
+ return VERDICT_SET.has(v);
16630
+ }
16631
+ var CANONICAL_VERDICTS, VERDICT_SET;
16632
+ var init_verdict = __esm({
16633
+ "../shared/platform/src/verdict.ts"() {
16634
+ "use strict";
16635
+ CANONICAL_VERDICTS = [
16636
+ "APPROVE",
16637
+ "REQUEST CHANGES",
16638
+ "NEEDS DISCUSSION"
16639
+ ];
16640
+ VERDICT_SET = new Set(CANONICAL_VERDICTS);
16641
+ }
16642
+ });
16643
+
16644
+ // ../shared/platform/src/counts.ts
16645
+ function deriveCounts(findings) {
16646
+ const counts = {
16647
+ blocker: 0,
16648
+ should_fix: 0,
16649
+ suggestion: 0,
16650
+ style: 0
16651
+ };
16652
+ for (const finding of findings) {
16653
+ const category = finding?.category;
16654
+ if (category === "blocker" || category === "should_fix" || category === "suggestion" || category === "style") {
16655
+ counts[category]++;
16656
+ }
16657
+ }
16658
+ return counts;
16659
+ }
16660
+ function collectFindings(meta) {
16661
+ const all = [];
16662
+ for (const reviewer of meta.reviewers ?? []) {
16663
+ for (const finding of reviewer?.findings ?? []) all.push(finding);
16664
+ }
16665
+ return all;
16666
+ }
16667
+ function preferred(scValue, derivedValue) {
16668
+ return typeof scValue === "number" && Number.isFinite(scValue) ? scValue : derivedValue;
16669
+ }
16670
+ function resolveRoundCounts(meta) {
16671
+ const allFindings = collectFindings(meta);
16672
+ const derived = deriveCounts(allFindings);
16673
+ const sc = meta.synthesis_counts ?? void 0;
16674
+ return {
16675
+ blockerCount: sc ? preferred(sc.blockers, derived.blocker) : derived.blocker,
16676
+ shouldFixCount: sc ? preferred(sc.should_fix, derived.should_fix) : derived.should_fix,
16677
+ suggestionCount: sc ? preferred(sc.suggestions, derived.suggestion) : derived.suggestion,
16678
+ reviewerCount: (meta.reviewers ?? []).length,
16679
+ totalFindingCount: allFindings.length
16680
+ };
16681
+ }
16682
+ var init_counts = __esm({
16683
+ "../shared/platform/src/counts.ts"() {
16684
+ "use strict";
16685
+ }
16686
+ });
16687
+
16688
+ // ../shared/platform/src/index.ts
16689
+ import { pathToFileURL } from "node:url";
16690
+ async function importModule(absolutePath) {
16691
+ return import(pathToFileURL(absolutePath).href);
16692
+ }
16693
+ function killErrorMeansDead(err) {
16694
+ return err instanceof Error && "code" in err && err.code === "ESRCH";
16695
+ }
16043
16696
  function isProcessAlive(pid) {
16044
16697
  try {
16045
16698
  process.kill(pid, 0);
16046
16699
  return true;
16047
16700
  } catch (err) {
16048
- return !(err instanceof Error && "code" in err && err.code === "ESRCH");
16701
+ return !killErrorMeansDead(err);
16049
16702
  }
16050
16703
  }
16051
16704
  function defaultIconFor(id, tier) {
@@ -16054,11 +16707,13 @@ function defaultIconFor(id, tier) {
16054
16707
  function hostCapabilitiesFor(vendor) {
16055
16708
  return vendor && HOST_CAPABILITIES[vendor] || DEFAULT_HOST_CAPABILITIES;
16056
16709
  }
16057
- var execFilePromise, isWindows, BUILTIN_ICON_MAP, DEFAULT_HOST_CAPABILITIES, HOST_CAPABILITIES;
16710
+ var isWindows, BUILTIN_ICON_MAP, DEFAULT_HOST_CAPABILITIES, HOST_CAPABILITIES;
16058
16711
  var init_src = __esm({
16059
16712
  "../shared/platform/src/index.ts"() {
16060
16713
  "use strict";
16061
- execFilePromise = promisify(execFile);
16714
+ init_spawn();
16715
+ init_verdict();
16716
+ init_counts();
16062
16717
  isWindows = process.platform === "win32";
16063
16718
  BUILTIN_ICON_MAP = {
16064
16719
  architect: "blocks",
@@ -23399,31 +24054,7 @@ var require_dist = __commonJS({
23399
24054
  }
23400
24055
  });
23401
24056
 
23402
- // src/lib/db/result-mapper.ts
23403
- function resultToRows(result) {
23404
- if (result.length === 0 || !result[0]) {
23405
- return [];
23406
- }
23407
- const { columns, values } = result[0];
23408
- return values.map((row) => {
23409
- const obj = {};
23410
- for (let i = 0; i < columns.length; i++) {
23411
- obj[columns[i]] = row[i];
23412
- }
23413
- return obj;
23414
- });
23415
- }
23416
- function resultToRow(result) {
23417
- const rows = resultToRows(result);
23418
- return rows[0];
23419
- }
23420
- var init_result_mapper = __esm({
23421
- "src/lib/db/result-mapper.ts"() {
23422
- "use strict";
23423
- }
23424
- });
23425
-
23426
- // src/lib/db/engine.ts
24057
+ // ../shared/persistence/src/db/engine.ts
23427
24058
  import { createRequire as createRequire2 } from "node:module";
23428
24059
  function applyEnginePreconditions() {
23429
24060
  if (_preconditionsApplied) return;
@@ -23477,7 +24108,7 @@ function openEngine(dbPath) {
23477
24108
  }
23478
24109
  var SQLITE_BUSY, SQLITE_BUSY_SNAPSHOT, BUSY_RETRY_ATTEMPTS, BUSY_RETRY_BACKOFF_MS, savepointName, nodeRequire, _preconditionsApplied, _DatabaseSyncCtor, SLEEP_BUF, NodeSqliteAdapter;
23479
24110
  var init_engine = __esm({
23480
- "src/lib/db/engine.ts"() {
24111
+ "../shared/persistence/src/db/engine.ts"() {
23481
24112
  "use strict";
23482
24113
  init_runtime_checks();
23483
24114
  SQLITE_BUSY = 5;
@@ -23604,7 +24235,7 @@ var init_engine = __esm({
23604
24235
  }
23605
24236
  });
23606
24237
 
23607
- // src/lib/db/migrations.ts
24238
+ // ../shared/persistence/src/db/migrations.ts
23608
24239
  function columnExists(db, table, column) {
23609
24240
  const result = db.exec(`PRAGMA table_info(${table})`);
23610
24241
  const first = result[0];
@@ -23659,7 +24290,7 @@ function runMigrations(db) {
23659
24290
  }
23660
24291
  var MIGRATIONS;
23661
24292
  var init_migrations = __esm({
23662
- "src/lib/db/migrations.ts"() {
24293
+ "../shared/persistence/src/db/migrations.ts"() {
23663
24294
  "use strict";
23664
24295
  MIGRATIONS = [
23665
24296
  {
@@ -24158,7 +24789,31 @@ var init_migrations = __esm({
24158
24789
  }
24159
24790
  });
24160
24791
 
24161
- // src/lib/db/queries.ts
24792
+ // ../shared/persistence/src/db/result-mapper.ts
24793
+ function resultToRows(result) {
24794
+ if (result.length === 0 || !result[0]) {
24795
+ return [];
24796
+ }
24797
+ const { columns, values } = result[0];
24798
+ return values.map((row) => {
24799
+ const obj = {};
24800
+ for (let i = 0; i < columns.length; i++) {
24801
+ obj[columns[i]] = row[i];
24802
+ }
24803
+ return obj;
24804
+ });
24805
+ }
24806
+ function resultToRow(result) {
24807
+ const rows = resultToRows(result);
24808
+ return rows[0];
24809
+ }
24810
+ var init_result_mapper = __esm({
24811
+ "../shared/persistence/src/db/result-mapper.ts"() {
24812
+ "use strict";
24813
+ }
24814
+ });
24815
+
24816
+ // ../shared/persistence/src/db/queries.ts
24162
24817
  function insertSession(db, params) {
24163
24818
  const {
24164
24819
  id,
@@ -24273,15 +24928,15 @@ function commitReasonClose(db, sessionId, reasonEvent, projectionUpdates) {
24273
24928
  });
24274
24929
  }
24275
24930
  var init_queries = __esm({
24276
- "src/lib/db/queries.ts"() {
24931
+ "../shared/persistence/src/db/queries.ts"() {
24277
24932
  "use strict";
24278
24933
  init_result_mapper();
24279
24934
  }
24280
24935
  });
24281
24936
 
24282
- // src/lib/db/reconcile.ts
24283
- import { existsSync as existsSync10 } from "node:fs";
24284
- import { isAbsolute as isAbsolute2, join as join12, dirname as dirname5 } from "node:path";
24937
+ // ../shared/persistence/src/db/reconcile.ts
24938
+ import { existsSync as existsSync7 } from "node:fs";
24939
+ import { isAbsolute as isAbsolute2, join as join9, dirname as dirname5 } from "node:path";
24285
24940
  function hasTerminalArtifactEvent(db, sessionId, workflowType, currentRound, currentMapRun) {
24286
24941
  const eventType = workflowType === "map" ? "map_completed" : "round_completed";
24287
24942
  const round = workflowType === "map" ? currentMapRun : currentRound;
@@ -24322,7 +24977,7 @@ function hasInFlightDependents(db, sessionId) {
24322
24977
  function resolveSessionDir(ocrDir, sessionDir) {
24323
24978
  if (!sessionDir) return null;
24324
24979
  if (isAbsolute2(sessionDir)) return sessionDir;
24325
- return join12(dirname5(ocrDir), sessionDir);
24980
+ return join9(dirname5(ocrDir), sessionDir);
24326
24981
  }
24327
24982
  function reconcileLegacyState(db, ocrDir, opts = {}) {
24328
24983
  const dryRun = opts.dryRun ?? false;
@@ -24334,8 +24989,8 @@ function reconcileLegacyState(db, ocrDir, opts = {}) {
24334
24989
  if (hasTerminalArtifactEvent(db, s.id, s.workflow_type, s.current_round, s.current_map_run) || hasReasonEvent(db, s.id)) {
24335
24990
  continue;
24336
24991
  }
24337
- const reviewFinal = s.workflow_type === "review" && dir ? existsSync10(join12(dir, "rounds", `round-${s.current_round}`, "final.md")) : false;
24338
- const mapFinal = s.workflow_type === "map" && dir ? existsSync10(join12(dir, "map", "runs", `run-${s.current_map_run}`, "map.md")) : false;
24992
+ const reviewFinal = s.workflow_type === "review" && dir ? existsSync7(join9(dir, "rounds", `round-${s.current_round}`, "final.md")) : false;
24993
+ const mapFinal = s.workflow_type === "map" && dir ? existsSync7(join9(dir, "map", "runs", `run-${s.current_map_run}`, "map.md")) : false;
24339
24994
  if (reviewFinal) {
24340
24995
  actions.push({
24341
24996
  sessionId: s.id,
@@ -24411,20 +25066,20 @@ function reconcileLegacyState(db, ocrDir, opts = {}) {
24411
25066
  }
24412
25067
  var DEFAULT_STALE_THRESHOLD_SECONDS;
24413
25068
  var init_reconcile = __esm({
24414
- "src/lib/db/reconcile.ts"() {
25069
+ "../shared/persistence/src/db/reconcile.ts"() {
24415
25070
  "use strict";
24416
25071
  init_queries();
24417
25072
  DEFAULT_STALE_THRESHOLD_SECONDS = 7 * 24 * 60 * 60;
24418
25073
  }
24419
25074
  });
24420
25075
 
24421
- // src/lib/db/liveness.ts
25076
+ // ../shared/persistence/src/db/liveness.ts
24422
25077
  function defaultIsAlive(pid) {
24423
25078
  try {
24424
25079
  process.kill(pid, 0);
24425
25080
  return true;
24426
25081
  } catch (err) {
24427
- return !(err instanceof Error && "code" in err && err.code === "ESRCH");
25082
+ return !killErrorMeansDead(err);
24428
25083
  }
24429
25084
  }
24430
25085
  function sqliteUtcMs(ts) {
@@ -24433,16 +25088,17 @@ function sqliteUtcMs(ts) {
24433
25088
  }
24434
25089
  var PID_REUSE_GUARD_MS;
24435
25090
  var init_liveness = __esm({
24436
- "src/lib/db/liveness.ts"() {
25091
+ "../shared/persistence/src/db/liveness.ts"() {
24437
25092
  "use strict";
25093
+ init_src();
24438
25094
  PID_REUSE_GUARD_MS = 24 * 60 * 60 * 1e3;
24439
25095
  }
24440
25096
  });
24441
25097
 
24442
- // src/lib/state/exit-codes.ts
25098
+ // ../shared/persistence/src/state/exit-codes.ts
24443
25099
  var STATE_EXIT, StateError, CANCELLED_EXIT_CODE, ORPHAN_EXIT_CODE, CASCADE_CLOSE_EXIT_CODE, WATCHDOG_DEADLINE_EXIT_CODE;
24444
25100
  var init_exit_codes = __esm({
24445
- "src/lib/state/exit-codes.ts"() {
25101
+ "../shared/persistence/src/state/exit-codes.ts"() {
24446
25102
  "use strict";
24447
25103
  STATE_EXIT = {
24448
25104
  OK: 0,
@@ -24469,7 +25125,7 @@ var init_exit_codes = __esm({
24469
25125
  }
24470
25126
  });
24471
25127
 
24472
- // src/lib/db/agent-sessions.ts
25128
+ // ../shared/persistence/src/db/agent-sessions.ts
24473
25129
  function cascadeTerminateExecutions(db, workflowId, exitCode, note) {
24474
25130
  db.run(
24475
25131
  `UPDATE command_executions
@@ -24640,6 +25296,9 @@ function bindVendorSessionIdOpportunistically(db, vendorSessionId) {
24640
25296
  );
24641
25297
  return candidate.uid ?? String(candidate.id);
24642
25298
  }
25299
+ function isSafeVendorSessionId(id) {
25300
+ return SAFE_VENDOR_SESSION_ID.test(id);
25301
+ }
24643
25302
  function recordVendorSessionIdForExecution(db, executionId, vendorSessionId) {
24644
25303
  db.run(
24645
25304
  `UPDATE command_executions
@@ -24835,9 +25494,9 @@ function sweepStaleSessions(db, thresholdSeconds) {
24835
25494
  }
24836
25495
  return { closedSessionIds: rows.map((r) => r.id) };
24837
25496
  }
24838
- var NOTE_ORPHAN_PREFIX, INSTANCE_COMMAND;
25497
+ var NOTE_ORPHAN_PREFIX, INSTANCE_COMMAND, SAFE_VENDOR_SESSION_ID;
24839
25498
  var init_agent_sessions = __esm({
24840
- "src/lib/db/agent-sessions.ts"() {
25499
+ "../shared/persistence/src/db/agent-sessions.ts"() {
24841
25500
  "use strict";
24842
25501
  init_result_mapper();
24843
25502
  init_queries();
@@ -24845,18 +25504,19 @@ var init_agent_sessions = __esm({
24845
25504
  init_exit_codes();
24846
25505
  NOTE_ORPHAN_PREFIX = "orphaned by liveness sweep";
24847
25506
  INSTANCE_COMMAND = "session-instance";
25507
+ SAFE_VENDOR_SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/;
24848
25508
  }
24849
25509
  });
24850
25510
 
24851
- // src/lib/db/maintenance.ts
25511
+ // ../shared/persistence/src/db/maintenance.ts
24852
25512
  import {
24853
- existsSync as existsSync11,
24854
- readdirSync as readdirSync5,
25513
+ existsSync as existsSync8,
25514
+ readdirSync as readdirSync2,
24855
25515
  statSync,
24856
25516
  unlinkSync as unlinkSync3,
24857
25517
  copyFileSync
24858
25518
  } from "node:fs";
24859
- import { dirname as dirname6, join as join13, basename as basename7 } from "node:path";
25519
+ import { dirname as dirname6, join as join10, basename as basename3 } from "node:path";
24860
25520
  function withForeignKeysDisabled(db, fn) {
24861
25521
  db.pragma("foreign_keys = OFF");
24862
25522
  try {
@@ -24883,7 +25543,7 @@ function foreignKeyViolationGroups(db) {
24883
25543
  function scanOrphanTempFiles(dataDir) {
24884
25544
  let entries;
24885
25545
  try {
24886
- entries = readdirSync5(dataDir);
25546
+ entries = readdirSync2(dataDir);
24887
25547
  } catch {
24888
25548
  return [];
24889
25549
  }
@@ -24894,7 +25554,7 @@ function scanOrphanTempFiles(dataDir) {
24894
25554
  const pid = Number(m[1]);
24895
25555
  let ageMs = 0;
24896
25556
  try {
24897
- ageMs = Date.now() - statSync(join13(dataDir, name)).mtimeMs;
25557
+ ageMs = Date.now() - statSync(join10(dataDir, name)).mtimeMs;
24898
25558
  } catch {
24899
25559
  continue;
24900
25560
  }
@@ -24913,7 +25573,7 @@ function scanOrphanTempFiles(dataDir) {
24913
25573
  function scanBackupFiles(dataDir, dbBase) {
24914
25574
  let entries;
24915
25575
  try {
24916
- entries = readdirSync5(dataDir);
25576
+ entries = readdirSync2(dataDir);
24917
25577
  } catch {
24918
25578
  return [];
24919
25579
  }
@@ -24921,7 +25581,7 @@ function scanBackupFiles(dataDir, dbBase) {
24921
25581
  for (const name of entries) {
24922
25582
  if (!name.startsWith(`${dbBase}.bak`)) continue;
24923
25583
  try {
24924
- out.push({ name, sizeBytes: statSync(join13(dataDir, name)).size });
25584
+ out.push({ name, sizeBytes: statSync(join10(dataDir, name)).size });
24925
25585
  } catch {
24926
25586
  }
24927
25587
  }
@@ -24929,7 +25589,7 @@ function scanBackupFiles(dataDir, dbBase) {
24929
25589
  }
24930
25590
  function collectDbHealth(db, dbPath) {
24931
25591
  const dataDir = dirname6(dbPath);
24932
- const dbBase = basename7(dbPath);
25592
+ const dbBase = basename3(dbPath);
24933
25593
  const pageSize = scalarInt(db, "PRAGMA page_size");
24934
25594
  const pageCount = scalarInt(db, "PRAGMA page_count");
24935
25595
  const freelistCount = scalarInt(db, "PRAGMA freelist_count");
@@ -24941,7 +25601,7 @@ function collectDbHealth(db, dbPath) {
24941
25601
  const protectedFkViolations = allGroups.filter(
24942
25602
  (g) => PROTECTED_TABLES.has(g.table)
24943
25603
  );
24944
- const fileSizeBytes = existsSync11(dbPath) ? statSync(dbPath).size : 0;
25604
+ const fileSizeBytes = existsSync8(dbPath) ? statSync(dbPath).size : 0;
24945
25605
  return {
24946
25606
  dbPath,
24947
25607
  fileSizeBytes,
@@ -24969,7 +25629,7 @@ function collectDbHealth(db, dbPath) {
24969
25629
  }
24970
25630
  function snapshotDb(db, dbPath, label = "doctor") {
24971
25631
  try {
24972
- if (!existsSync11(dbPath) || statSync(dbPath).size === 0) return null;
25632
+ if (!existsSync8(dbPath) || statSync(dbPath).size === 0) return null;
24973
25633
  db.pragma("wal_checkpoint(TRUNCATE)");
24974
25634
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
24975
25635
  const bakPath = `${dbPath}.bak.${label}.${ts}`;
@@ -24984,7 +25644,7 @@ function reapOrphanDbFiles(dataDir) {
24984
25644
  for (const f of scanOrphanTempFiles(dataDir)) {
24985
25645
  if (!f.reapable) continue;
24986
25646
  try {
24987
- unlinkSync3(join13(dataDir, f.name));
25647
+ unlinkSync3(join10(dataDir, f.name));
24988
25648
  reaped.push(f.name);
24989
25649
  } catch {
24990
25650
  }
@@ -24994,7 +25654,7 @@ function reapOrphanDbFiles(dataDir) {
24994
25654
  function reapStaleExecLogs(execLogsDir, maxAgeMs = SEVEN_DAYS_MS) {
24995
25655
  let entries;
24996
25656
  try {
24997
- entries = readdirSync5(execLogsDir);
25657
+ entries = readdirSync2(execLogsDir);
24998
25658
  } catch {
24999
25659
  return [];
25000
25660
  }
@@ -25002,7 +25662,7 @@ function reapStaleExecLogs(execLogsDir, maxAgeMs = SEVEN_DAYS_MS) {
25002
25662
  const reaped = [];
25003
25663
  for (const name of entries) {
25004
25664
  if (!name.endsWith(".log")) continue;
25005
- const full = join13(execLogsDir, name);
25665
+ const full = join10(execLogsDir, name);
25006
25666
  try {
25007
25667
  if (statSync(full).mtimeMs > cutoff) continue;
25008
25668
  unlinkSync3(full);
@@ -25020,11 +25680,11 @@ function pruneBackups(dataDir, dbPath, opts = {}) {
25020
25680
  );
25021
25681
  }
25022
25682
  const dryRun = opts.dryRun ?? false;
25023
- const dbBase = basename7(dbPath);
25683
+ const dbBase = basename3(dbPath);
25024
25684
  const withMtime = [];
25025
25685
  for (const file of scanBackupFiles(dataDir, dbBase)) {
25026
25686
  try {
25027
- withMtime.push({ file, mtimeMs: statSync(join13(dataDir, file.name)).mtimeMs });
25687
+ withMtime.push({ file, mtimeMs: statSync(join10(dataDir, file.name)).mtimeMs });
25028
25688
  } catch {
25029
25689
  }
25030
25690
  }
@@ -25035,7 +25695,7 @@ function pruneBackups(dataDir, dbPath, opts = {}) {
25035
25695
  if (!dryRun) {
25036
25696
  for (const b of toDelete) {
25037
25697
  try {
25038
- unlinkSync3(join13(dataDir, b.name));
25698
+ unlinkSync3(join10(dataDir, b.name));
25039
25699
  deleted.push(b);
25040
25700
  } catch {
25041
25701
  }
@@ -25051,7 +25711,7 @@ function pruneBackups(dataDir, dbPath, opts = {}) {
25051
25711
  }
25052
25712
  function fixDb(db, dbPath, opts = {}) {
25053
25713
  const dataDir = dirname6(dbPath);
25054
- const sizeBeforeBytes = existsSync11(dbPath) ? statSync(dbPath).size : 0;
25714
+ const sizeBeforeBytes = existsSync8(dbPath) ? statSync(dbPath).size : 0;
25055
25715
  const snapshotPath = opts.snapshot === false ? null : snapshotDb(db, dbPath, "doctor");
25056
25716
  const fkOrphansDeleted = [];
25057
25717
  withForeignKeysDisabled(db, () => {
@@ -25095,12 +25755,12 @@ function fixDb(db, dbPath, opts = {}) {
25095
25755
  };
25096
25756
  }
25097
25757
  function vacuumDb(db, dbPath, opts = {}) {
25098
- const sizeBeforeBytes = existsSync11(dbPath) ? statSync(dbPath).size : 0;
25758
+ const sizeBeforeBytes = existsSync8(dbPath) ? statSync(dbPath).size : 0;
25099
25759
  const snapshotPath = opts.snapshot === false ? null : snapshotDb(db, dbPath, "vacuum");
25100
25760
  db.pragma("wal_checkpoint(TRUNCATE)");
25101
25761
  db.run("VACUUM");
25102
25762
  db.pragma("wal_checkpoint(TRUNCATE)");
25103
- const sizeAfterBytes = existsSync11(dbPath) ? statSync(dbPath).size : 0;
25763
+ const sizeAfterBytes = existsSync8(dbPath) ? statSync(dbPath).size : 0;
25104
25764
  return {
25105
25765
  snapshotPath,
25106
25766
  sizeBeforeBytes,
@@ -25180,7 +25840,7 @@ function pruneDb(db, dbPath, opts = {}) {
25180
25840
  }
25181
25841
  var PROTECTED_TABLES, ORPHAN_SWEEPS, MARKDOWN_DEDUP_SQL, ONE_HOUR_MS, SEVEN_DAYS_MS;
25182
25842
  var init_maintenance = __esm({
25183
- "src/lib/db/maintenance.ts"() {
25843
+ "../shared/persistence/src/db/maintenance.ts"() {
25184
25844
  "use strict";
25185
25845
  init_src();
25186
25846
  PROTECTED_TABLES = /* @__PURE__ */ new Set([
@@ -25255,24 +25915,24 @@ var init_maintenance = __esm({
25255
25915
  }
25256
25916
  });
25257
25917
 
25258
- // src/lib/db/command-log.ts
25259
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync, writeFileSync as writeFileSync6 } from "node:fs";
25260
- import { dirname as dirname7, join as join14 } from "node:path";
25918
+ // ../shared/persistence/src/db/command-log.ts
25919
+ import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync, writeFileSync as writeFileSync6 } from "node:fs";
25920
+ import { dirname as dirname7, join as join11 } from "node:path";
25261
25921
  import { randomUUID as randomUUID2 } from "node:crypto";
25262
25922
  function generateCommandUid() {
25263
25923
  return randomUUID2();
25264
25924
  }
25265
25925
  function cacheDir(ocrDir) {
25266
- return join14(ocrDir, "data", CACHE_DIR);
25926
+ return join11(ocrDir, "data", CACHE_DIR);
25267
25927
  }
25268
25928
  function commandLogPath(ocrDir) {
25269
- return join14(cacheDir(ocrDir), FILENAME);
25929
+ return join11(cacheDir(ocrDir), FILENAME);
25270
25930
  }
25271
25931
  function appendCommandLog(ocrDir, entry) {
25272
25932
  try {
25273
25933
  const filePath = commandLogPath(ocrDir);
25274
25934
  const dir = dirname7(filePath);
25275
- if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true });
25935
+ if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
25276
25936
  const line = JSON.stringify(entry) + "\n";
25277
25937
  appendFileSync(filePath, line, { encoding: "utf-8" });
25278
25938
  if (approxLineCount >= 0) approxLineCount++;
@@ -25282,8 +25942,8 @@ function appendCommandLog(ocrDir, entry) {
25282
25942
  }
25283
25943
  function readCommandLog(ocrDir) {
25284
25944
  const filePath = commandLogPath(ocrDir);
25285
- if (!existsSync12(filePath)) return [];
25286
- const content = readFileSync9(filePath, "utf-8");
25945
+ if (!existsSync9(filePath)) return [];
25946
+ const content = readFileSync7(filePath, "utf-8");
25287
25947
  const entries = [];
25288
25948
  for (const line of content.split("\n")) {
25289
25949
  if (!line.trim()) continue;
@@ -25334,7 +25994,7 @@ function replayCommandLog(db, ocrDir) {
25334
25994
  function rotateIfNeeded(filePath) {
25335
25995
  try {
25336
25996
  if (approxLineCount >= 0 && approxLineCount <= MAX_LINES) return;
25337
- const content = readFileSync9(filePath, "utf-8");
25997
+ const content = readFileSync7(filePath, "utf-8");
25338
25998
  const lines = content.split("\n").filter((l) => l.trim());
25339
25999
  approxLineCount = lines.length;
25340
26000
  if (approxLineCount <= MAX_LINES) return;
@@ -25348,7 +26008,7 @@ function rotateIfNeeded(filePath) {
25348
26008
  }
25349
26009
  var CACHE_DIR, FILENAME, MAX_LINES, KEEP_LINES, approxLineCount;
25350
26010
  var init_command_log = __esm({
25351
- "src/lib/db/command-log.ts"() {
26011
+ "../shared/persistence/src/db/command-log.ts"() {
25352
26012
  "use strict";
25353
26013
  CACHE_DIR = ".cache";
25354
26014
  FILENAME = "command-history.jsonl";
@@ -25358,7 +26018,7 @@ var init_command_log = __esm({
25358
26018
  }
25359
26019
  });
25360
26020
 
25361
- // src/lib/db/index.ts
26021
+ // ../shared/persistence/src/db/index.ts
25362
26022
  var db_exports = {};
25363
26023
  __export(db_exports, {
25364
26024
  CANCELLED_EXIT_CODE: () => CANCELLED_EXIT_CODE,
@@ -25366,6 +26026,7 @@ __export(db_exports, {
25366
26026
  MIGRATIONS: () => MIGRATIONS,
25367
26027
  ORPHAN_EXIT_CODE: () => ORPHAN_EXIT_CODE,
25368
26028
  PID_REUSE_GUARD_MS: () => PID_REUSE_GUARD_MS,
26029
+ SAFE_VENDOR_SESSION_ID: () => SAFE_VENDOR_SESSION_ID,
25369
26030
  STATE_EXIT: () => STATE_EXIT,
25370
26031
  StateError: () => StateError,
25371
26032
  WATCHDOG_DEADLINE_EXIT_CODE: () => WATCHDOG_DEADLINE_EXIT_CODE,
@@ -25398,6 +26059,7 @@ __export(db_exports, {
25398
26059
  insertEvent: () => insertEvent,
25399
26060
  insertSession: () => insertSession,
25400
26061
  isBusyError: () => isBusyError,
26062
+ isSafeVendorSessionId: () => isSafeVendorSessionId,
25401
26063
  linkDashboardInvocationToWorkflow: () => linkDashboardInvocationToWorkflow,
25402
26064
  listAgentSessionsForWorkflow: () => listAgentSessionsForWorkflow,
25403
26065
  openDatabase: () => openDatabase,
@@ -25428,7 +26090,7 @@ __export(db_exports, {
25428
26090
  withForeignKeysDisabled: () => withForeignKeysDisabled
25429
26091
  });
25430
26092
  import {
25431
- existsSync as existsSync13,
26093
+ existsSync as existsSync10,
25432
26094
  mkdirSync as mkdirSync5,
25433
26095
  copyFileSync as copyFileSync2,
25434
26096
  statSync as statSync2,
@@ -25436,13 +26098,13 @@ import {
25436
26098
  rmSync
25437
26099
  } from "node:fs";
25438
26100
  import { tmpdir } from "node:os";
25439
- import { dirname as dirname8, join as join15 } from "node:path";
26101
+ import { dirname as dirname8, join as join12 } from "node:path";
25440
26102
  function maybeSnapshotBeforeUpgrade(db, dbPath, fromVersion) {
25441
26103
  if (fromVersion < 1 || fromVersion >= V2_SCHEMA_VERSION) return null;
25442
26104
  const bakPath = `${dbPath}.bak.v${fromVersion}`;
25443
- if (existsSync13(bakPath)) return bakPath;
26105
+ if (existsSync10(bakPath)) return bakPath;
25444
26106
  try {
25445
- if (!existsSync13(dbPath) || statSync2(dbPath).size === 0) return null;
26107
+ if (!existsSync10(dbPath) || statSync2(dbPath).size === 0) return null;
25446
26108
  db.pragma("wal_checkpoint(TRUNCATE)");
25447
26109
  copyFileSync2(dbPath, bakPath);
25448
26110
  return bakPath;
@@ -25478,7 +26140,7 @@ async function openDatabase(dbPath) {
25478
26140
  return cached;
25479
26141
  }
25480
26142
  const dir = dirname8(dbPath);
25481
- if (!existsSync13(dir)) {
26143
+ if (!existsSync10(dir)) {
25482
26144
  mkdirSync5(dir, { recursive: true });
25483
26145
  }
25484
26146
  const db = openEngine(dbPath);
@@ -25486,15 +26148,15 @@ async function openDatabase(dbPath) {
25486
26148
  return db;
25487
26149
  }
25488
26150
  async function getDb(ocrDir) {
25489
- const dbPath = join15(ocrDir, "data", "ocr.db");
26151
+ const dbPath = join12(ocrDir, "data", "ocr.db");
25490
26152
  return openDatabase(dbPath);
25491
26153
  }
25492
26154
  async function ensureDatabase(ocrDir) {
25493
- const dataDir = join15(ocrDir, "data");
25494
- if (!existsSync13(dataDir)) {
26155
+ const dataDir = join12(ocrDir, "data");
26156
+ if (!existsSync10(dataDir)) {
25495
26157
  mkdirSync5(dataDir, { recursive: true });
25496
26158
  }
25497
- const dbPath = join15(dataDir, "ocr.db");
26159
+ const dbPath = join12(dataDir, "ocr.db");
25498
26160
  const db = await openDatabase(dbPath);
25499
26161
  let before = 0;
25500
26162
  try {
@@ -25522,7 +26184,7 @@ async function ensureDatabase(ocrDir) {
25522
26184
  return db;
25523
26185
  }
25524
26186
  function walCheckpointTruncate(dbPath) {
25525
- if (!existsSync13(dbPath)) {
26187
+ if (!existsSync10(dbPath)) {
25526
26188
  return "skipped";
25527
26189
  }
25528
26190
  const cached = connections.get(dbPath);
@@ -25564,8 +26226,8 @@ function closeAllDatabases() {
25564
26226
  function probeWrite() {
25565
26227
  let dir;
25566
26228
  try {
25567
- dir = mkdtempSync(join15(tmpdir(), "ocr-probe-"));
25568
- const db = openEngine(join15(dir, "probe.db"));
26229
+ dir = mkdtempSync(join12(tmpdir(), "ocr-probe-"));
26230
+ const db = openEngine(join12(dir, "probe.db"));
25569
26231
  try {
25570
26232
  db.run("CREATE TABLE _probe_write (id INTEGER PRIMARY KEY, v TEXT)");
25571
26233
  db.transaction(() => {
@@ -25598,7 +26260,7 @@ function rmDirBestEffort(dir) {
25598
26260
  }
25599
26261
  var V2_SCHEMA_VERSION, connections;
25600
26262
  var init_db = __esm({
25601
- "src/lib/db/index.ts"() {
26263
+ "../shared/persistence/src/db/index.ts"() {
25602
26264
  "use strict";
25603
26265
  init_engine();
25604
26266
  init_migrations();
@@ -26367,7 +27029,7 @@ if (process.platform === "linux") {
26367
27029
  // ../../node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.js
26368
27030
  var processOk = (process13) => !!process13 && typeof process13 === "object" && typeof process13.removeListener === "function" && typeof process13.emit === "function" && typeof process13.reallyExit === "function" && typeof process13.listeners === "function" && typeof process13.kill === "function" && typeof process13.pid === "number" && typeof process13.on === "function";
26369
27031
  var kExitEmitter = Symbol.for("signal-exit emitter");
26370
- var global = globalThis;
27032
+ var global2 = globalThis;
26371
27033
  var ObjectDefineProperty = Object.defineProperty.bind(Object);
26372
27034
  var Emitter = class {
26373
27035
  emitted = {
@@ -26381,10 +27043,10 @@ var Emitter = class {
26381
27043
  count = 0;
26382
27044
  id = Math.random();
26383
27045
  constructor() {
26384
- if (global[kExitEmitter]) {
26385
- return global[kExitEmitter];
27046
+ if (global2[kExitEmitter]) {
27047
+ return global2[kExitEmitter];
26386
27048
  }
26387
- ObjectDefineProperty(global, kExitEmitter, {
27049
+ ObjectDefineProperty(global2, kExitEmitter, {
26388
27050
  value: this,
26389
27051
  writable: false,
26390
27052
  enumerable: false,
@@ -29358,10 +30020,11 @@ function ensureGitignore(ocrDir) {
29358
30020
  writeFileSync2(gitignorePath, content);
29359
30021
  }
29360
30022
 
29361
- // src/lib/team-config.ts
30023
+ // ../shared/config/src/team-config.ts
29362
30024
  var import_yaml = __toESM(require_dist(), 1);
29363
30025
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
29364
30026
  import { join as join2 } from "node:path";
30027
+ var MAX_INSTANCES_PER_PERSONA = 50;
29365
30028
  function loadTeamConfig(ocrDir) {
29366
30029
  const configPath = join2(ocrDir, "config.yaml");
29367
30030
  if (!existsSync2(configPath)) {
@@ -29403,6 +30066,9 @@ function parseTeamConfigYaml(content) {
29403
30066
  aliases,
29404
30067
  defaultModel
29405
30068
  );
30069
+ if (resolvedModel !== null) {
30070
+ assertSafeModelId(resolvedModel, `default_team.${persona}[${i}]`);
30071
+ }
29406
30072
  team.push({
29407
30073
  persona,
29408
30074
  instance_index: i + 1,
@@ -29420,6 +30086,11 @@ function parseEntry(persona, entry) {
29420
30086
  `default_team.${persona}: count must be a positive integer (got ${entry})`
29421
30087
  );
29422
30088
  }
30089
+ if (entry > MAX_INSTANCES_PER_PERSONA) {
30090
+ throw new Error(
30091
+ `default_team.${persona}: count must be <= ${MAX_INSTANCES_PER_PERSONA} (got ${entry})`
30092
+ );
30093
+ }
29423
30094
  return Array.from({ length: entry }, () => ({}));
29424
30095
  }
29425
30096
  if (Array.isArray(entry)) {
@@ -29428,6 +30099,11 @@ function parseEntry(persona, entry) {
29428
30099
  `default_team.${persona}: list form must contain at least one instance`
29429
30100
  );
29430
30101
  }
30102
+ if (entry.length > MAX_INSTANCES_PER_PERSONA) {
30103
+ throw new Error(
30104
+ `default_team.${persona}: list form must contain <= ${MAX_INSTANCES_PER_PERSONA} instances (got ${entry.length})`
30105
+ );
30106
+ }
29431
30107
  return entry.map((item, idx) => parseListItem(persona, idx, item));
29432
30108
  }
29433
30109
  if (entry && typeof entry === "object") {
@@ -29449,6 +30125,11 @@ function parseEntry(persona, entry) {
29449
30125
  `default_team.${persona}: count must be a positive integer (got ${String(count)})`
29450
30126
  );
29451
30127
  }
30128
+ if (count > MAX_INSTANCES_PER_PERSONA) {
30129
+ throw new Error(
30130
+ `default_team.${persona}: count must be <= ${MAX_INSTANCES_PER_PERSONA} (got ${count})`
30131
+ );
30132
+ }
29452
30133
  const teamModel = readOptionalString(obj, "model", `default_team.${persona}.model`);
29453
30134
  return Array.from({ length: count }, () => ({ teamModel }));
29454
30135
  }
@@ -29483,6 +30164,16 @@ function readOptionalString(obj, key, pathLabel) {
29483
30164
  }
29484
30165
  return value;
29485
30166
  }
30167
+ var SAFE_MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._/:@[\]+-]{0,255}$/;
30168
+ function assertSafeModelId(value, pathLabel) {
30169
+ if (SAFE_MODEL_ID.test(value)) return;
30170
+ const allowed = /[A-Za-z0-9._/:@[\]+-]/;
30171
+ const offending = [...value].find((ch) => !allowed.test(ch));
30172
+ const detail = value.length === 0 ? "empty string" : value.length > 256 ? "longer than 256 characters" : offending !== void 0 ? `contains ${JSON.stringify(offending)}` : `starts with ${JSON.stringify(value[0])}`;
30173
+ throw new Error(
30174
+ `${pathLabel}: model id ${detail} \u2014 no vendor model id uses that. Allowed: letters and digits plus . _ / : @ [ ] + - (max 256 chars).`
30175
+ );
30176
+ }
29486
30177
  function readAliases(root) {
29487
30178
  const models = root["models"];
29488
30179
  if (!models || typeof models !== "object" || Array.isArray(models)) return {};
@@ -29524,10 +30215,78 @@ function resolveTeamComposition(team, override) {
29524
30215
  result.push(inst);
29525
30216
  }
29526
30217
  for (const inst of override) {
30218
+ if (inst.model !== null) {
30219
+ assertSafeModelId(inst.model, `override ${inst.persona}#${inst.instance_index}`);
30220
+ }
29527
30221
  result.push(inst);
29528
30222
  }
29529
30223
  return result;
29530
30224
  }
30225
+ var REVIEWER_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
30226
+ function parseTeamSpec(spec, aliases = {}, defaultModel = null) {
30227
+ const trimmed = spec.trim();
30228
+ if (trimmed.length === 0) {
30229
+ throw new Error(
30230
+ "--team spec is empty; expected reviewer-id:count[,reviewer-id:count...]"
30231
+ );
30232
+ }
30233
+ const model = resolveModel(null, null, aliases, defaultModel);
30234
+ if (model !== null) assertSafeModelId(model, "--team");
30235
+ const seen = /* @__PURE__ */ new Set();
30236
+ const result = [];
30237
+ for (const rawEntry of trimmed.split(",")) {
30238
+ const entry = rawEntry.trim();
30239
+ if (entry.length === 0) {
30240
+ throw new Error(
30241
+ `--team has an empty entry (stray or trailing comma) in "${spec}"`
30242
+ );
30243
+ }
30244
+ const colon = entry.indexOf(":");
30245
+ if (colon === -1) {
30246
+ throw new Error(
30247
+ `--team entry "${entry}" must be "reviewer-id:count" \u2014 the :count is required`
30248
+ );
30249
+ }
30250
+ const persona = entry.slice(0, colon).trim();
30251
+ const countRaw = entry.slice(colon + 1).trim();
30252
+ if (!REVIEWER_ID_PATTERN.test(persona)) {
30253
+ throw new Error(
30254
+ `--team reviewer id "${persona}" is invalid; expected lowercase letters, digits, and single hyphens (e.g. principal, martin-fowler)`
30255
+ );
30256
+ }
30257
+ if (seen.has(persona)) {
30258
+ throw new Error(
30259
+ `--team lists "${persona}" more than once; combine its instances into a single entry (e.g. ${persona}:2)`
30260
+ );
30261
+ }
30262
+ if (!/^[0-9]+$/.test(countRaw)) {
30263
+ throw new Error(
30264
+ `--team count for "${persona}" must be a positive integer (got "${countRaw}")`
30265
+ );
30266
+ }
30267
+ const count = Number(countRaw);
30268
+ if (count < 1) {
30269
+ throw new Error(
30270
+ `--team count for "${persona}" must be a positive integer (got ${count})`
30271
+ );
30272
+ }
30273
+ if (count > MAX_INSTANCES_PER_PERSONA) {
30274
+ throw new Error(
30275
+ `--team count for "${persona}" must be <= ${MAX_INSTANCES_PER_PERSONA} (got ${count})`
30276
+ );
30277
+ }
30278
+ seen.add(persona);
30279
+ for (let i = 0; i < count; i++) {
30280
+ result.push({
30281
+ persona,
30282
+ instance_index: i + 1,
30283
+ name: `${persona}-${i + 1}`,
30284
+ model
30285
+ });
30286
+ }
30287
+ }
30288
+ return result;
30289
+ }
29531
30290
 
29532
30291
  // src/lib/installer.ts
29533
30292
  init_src();
@@ -30092,7 +30851,7 @@ ${hint}
30092
30851
  }
30093
30852
 
30094
30853
  // src/lib/version.ts
30095
- var CLI_VERSION = true ? "2.2.1" : createRequire(import.meta.url)("../../package.json").version;
30854
+ var CLI_VERSION = true ? "2.4.0" : createRequire(import.meta.url)("../../package.json").version;
30096
30855
 
30097
30856
  // src/lib/deps.ts
30098
30857
  init_src();
@@ -33026,12 +33785,12 @@ function getStrategy(workflowType) {
33026
33785
  }
33027
33786
 
33028
33787
  // src/lib/progress/detector.ts
33029
- import { existsSync as existsSync7, readdirSync as readdirSync2 } from "node:fs";
33030
- import { join as join9, basename as basename4 } from "node:path";
33788
+ import { existsSync as existsSync11, readdirSync as readdirSync3 } from "node:fs";
33789
+ import { join as join13, basename as basename5 } from "node:path";
33031
33790
 
33032
33791
  // src/lib/progress/session-reader.ts
33033
- init_result_mapper();
33034
- import { basename as basename3 } from "node:path";
33792
+ init_db();
33793
+ import { basename as basename4 } from "node:path";
33035
33794
  var cachedDb = null;
33036
33795
  function setProgressDb(db) {
33037
33796
  cachedDb = db;
@@ -33050,7 +33809,7 @@ function readSessionState(sessionPath) {
33050
33809
  }
33051
33810
  }
33052
33811
  function readFromSqlite(sessionPath, db) {
33053
- const sessionId = basename3(sessionPath);
33812
+ const sessionId = basename4(sessionPath);
33054
33813
  let row = resultToRow(
33055
33814
  db.exec("SELECT * FROM sessions WHERE id = ?", [sessionId])
33056
33815
  );
@@ -33085,7 +33844,7 @@ function detectWorkflowType(sessionPath, explicitType) {
33085
33844
  const db = getProgressDb();
33086
33845
  if (db) {
33087
33846
  try {
33088
- const sessionId = basename4(sessionPath);
33847
+ const sessionId = basename5(sessionPath);
33089
33848
  const result = db.exec(
33090
33849
  "SELECT workflow_type FROM sessions WHERE id = ?",
33091
33850
  [sessionId]
@@ -33098,8 +33857,8 @@ function detectWorkflowType(sessionPath, explicitType) {
33098
33857
  } catch {
33099
33858
  }
33100
33859
  }
33101
- const hasMapDir = existsSync7(join9(sessionPath, "map"));
33102
- const hasRoundsDir = existsSync7(join9(sessionPath, "rounds"));
33860
+ const hasMapDir = existsSync11(join13(sessionPath, "map"));
33861
+ const hasRoundsDir = existsSync11(join13(sessionPath, "rounds"));
33103
33862
  if (hasMapDir && !hasRoundsDir) {
33104
33863
  return "map";
33105
33864
  }
@@ -33109,7 +33868,7 @@ function detectWorkflowType(sessionPath, explicitType) {
33109
33868
  if (hasMapDir && hasRoundsDir) {
33110
33869
  if (db) {
33111
33870
  try {
33112
- const sessionId = basename4(sessionPath);
33871
+ const sessionId = basename5(sessionPath);
33113
33872
  const result = db.exec(
33114
33873
  "SELECT current_phase FROM sessions WHERE id = ?",
33115
33874
  [sessionId]
@@ -33133,7 +33892,7 @@ function isSessionActive(sessionPath) {
33133
33892
  const db = getProgressDb();
33134
33893
  if (db) {
33135
33894
  try {
33136
- const sessionId = basename4(sessionPath);
33895
+ const sessionId = basename5(sessionPath);
33137
33896
  const result = db.exec(
33138
33897
  "SELECT status, current_phase FROM sessions WHERE id = ?",
33139
33898
  [sessionId]
@@ -33155,28 +33914,28 @@ function isSessionActive(sessionPath) {
33155
33914
  }
33156
33915
  function detectActiveWorkflows(sessionPath) {
33157
33916
  const activeWorkflows = [];
33158
- const hasRoundsDir = existsSync7(join9(sessionPath, "rounds"));
33917
+ const hasRoundsDir = existsSync11(join13(sessionPath, "rounds"));
33159
33918
  if (hasRoundsDir) {
33160
- const roundsDir = join9(sessionPath, "rounds");
33161
- const rounds = existsSync7(roundsDir) ? readdirSync2(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort() : [];
33919
+ const roundsDir = join13(sessionPath, "rounds");
33920
+ const rounds = existsSync11(roundsDir) ? readdirSync3(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort() : [];
33162
33921
  if (rounds.length > 0) {
33163
33922
  const latestRound = rounds[rounds.length - 1];
33164
- const finalPath = join9(roundsDir, latestRound, "final.md");
33165
- if (!existsSync7(finalPath)) {
33923
+ const finalPath = join13(roundsDir, latestRound, "final.md");
33924
+ if (!existsSync11(finalPath)) {
33166
33925
  activeWorkflows.push("review");
33167
33926
  }
33168
33927
  } else {
33169
33928
  activeWorkflows.push("review");
33170
33929
  }
33171
33930
  }
33172
- const hasMapDir = existsSync7(join9(sessionPath, "map"));
33931
+ const hasMapDir = existsSync11(join13(sessionPath, "map"));
33173
33932
  if (hasMapDir) {
33174
- const runsDir = join9(sessionPath, "map", "runs");
33175
- const runs = existsSync7(runsDir) ? readdirSync2(runsDir).filter((d) => d.match(/^run-\d+$/)).sort() : [];
33933
+ const runsDir = join13(sessionPath, "map", "runs");
33934
+ const runs = existsSync11(runsDir) ? readdirSync3(runsDir).filter((d) => d.match(/^run-\d+$/)).sort() : [];
33176
33935
  if (runs.length > 0) {
33177
33936
  const latestRun = runs[runs.length - 1];
33178
- const mapPath = join9(runsDir, latestRun, "map.md");
33179
- if (!existsSync7(mapPath)) {
33937
+ const mapPath = join13(runsDir, latestRun, "map.md");
33938
+ if (!existsSync11(mapPath)) {
33180
33939
  activeWorkflows.push("map");
33181
33940
  }
33182
33941
  } else {
@@ -33187,7 +33946,7 @@ function detectActiveWorkflows(sessionPath) {
33187
33946
  const db = getProgressDb();
33188
33947
  if (db) {
33189
33948
  try {
33190
- const sessionId = basename4(sessionPath);
33949
+ const sessionId = basename5(sessionPath);
33191
33950
  const result = db.exec(
33192
33951
  "SELECT workflow_type, current_phase FROM sessions WHERE id = ?",
33193
33952
  [sessionId]
@@ -33255,8 +34014,8 @@ function padLines(lines) {
33255
34014
  }
33256
34015
 
33257
34016
  // src/lib/progress/review-strategy.ts
33258
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "node:fs";
33259
- import { join as join10, basename as basename5 } from "node:path";
34017
+ import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "node:fs";
34018
+ import { join as join14, basename as basename6 } from "node:path";
33260
34019
  var REVIEW_PHASES = [
33261
34020
  { key: "context", label: "Context Discovery" },
33262
34021
  { key: "change-context", label: "Change Context" },
@@ -33268,10 +34027,10 @@ var REVIEW_PHASES = [
33268
34027
  { key: "complete", label: "Complete" }
33269
34028
  ];
33270
34029
  function countFindings(filePath) {
33271
- if (!existsSync8(filePath)) {
34030
+ if (!existsSync12(filePath)) {
33272
34031
  return 0;
33273
34032
  }
33274
- const content = readFileSync7(filePath, "utf-8");
34033
+ const content = readFileSync8(filePath, "utf-8");
33275
34034
  const findingMatches = content.match(/^##\s+(Finding|Issue|Suggestion)/gm);
33276
34035
  return findingMatches?.length ?? 0;
33277
34036
  }
@@ -33285,27 +34044,27 @@ function formatReviewerName(filename) {
33285
34044
  return base.charAt(0).toUpperCase() + base.slice(1);
33286
34045
  }
33287
34046
  function deriveRoundsFromFilesystem(roundsDir) {
33288
- if (!existsSync8(roundsDir)) {
34047
+ if (!existsSync12(roundsDir)) {
33289
34048
  return [];
33290
34049
  }
33291
- const roundDirs = readdirSync3(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort((a, b) => {
34050
+ const roundDirs = readdirSync4(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort((a, b) => {
33292
34051
  const numA = parseInt(a.replace("round-", ""));
33293
34052
  const numB = parseInt(b.replace("round-", ""));
33294
34053
  return numA - numB;
33295
34054
  });
33296
34055
  return roundDirs.map((dir) => {
33297
34056
  const roundNum = parseInt(dir.replace("round-", ""));
33298
- const roundPath = join10(roundsDir, dir);
33299
- const reviewsPath = join10(roundPath, "reviews");
33300
- const finalPath = join10(roundPath, "final.md");
34057
+ const roundPath = join14(roundsDir, dir);
34058
+ const reviewsPath = join14(roundPath, "reviews");
34059
+ const finalPath = join14(roundPath, "final.md");
33301
34060
  const reviewers = [];
33302
- if (existsSync8(reviewsPath)) {
33303
- const files = readdirSync3(reviewsPath).filter((f) => f.endsWith(".md"));
34061
+ if (existsSync12(reviewsPath)) {
34062
+ const files = readdirSync4(reviewsPath).filter((f) => f.endsWith(".md"));
33304
34063
  reviewers.push(...files.map((f) => f.replace(".md", "")));
33305
34064
  }
33306
34065
  return {
33307
34066
  round: roundNum,
33308
- isComplete: existsSync8(finalPath),
34067
+ isComplete: existsSync12(finalPath),
33309
34068
  reviewers
33310
34069
  };
33311
34070
  });
@@ -33315,7 +34074,7 @@ var ReviewProgressStrategy = class {
33315
34074
  phases = REVIEW_PHASES;
33316
34075
  totalPhases = 8;
33317
34076
  parseState(sessionPath, preservedStartTime) {
33318
- const session = basename5(sessionPath);
34077
+ const session = basename6(sessionPath);
33319
34078
  const state = readSessionState(sessionPath);
33320
34079
  if (!state) {
33321
34080
  return null;
@@ -33334,19 +34093,19 @@ var ReviewProgressStrategy = class {
33334
34093
  parseFromState(session, state, sessionPath, preservedStartTime) {
33335
34094
  const effectiveStartTime = state.round_started_at ?? state.started_at;
33336
34095
  const startTime = preservedStartTime ?? (effectiveStartTime ? new Date(effectiveStartTime).getTime() : Date.now());
33337
- const roundsDir = join10(sessionPath, "rounds");
34096
+ const roundsDir = join14(sessionPath, "rounds");
33338
34097
  const rounds = deriveRoundsFromFilesystem(roundsDir);
33339
34098
  const highestExistingRound = rounds.length > 0 ? Math.max(...rounds.map((r) => r.round)) : 1;
33340
34099
  const stateRound = state.current_round ?? 1;
33341
34100
  const currentRound = Math.min(stateRound, highestExistingRound);
33342
- const currentRoundDir = join10(roundsDir, `round-${currentRound}`);
33343
- const reviewsDir = join10(currentRoundDir, "reviews");
34101
+ const currentRoundDir = join14(roundsDir, `round-${currentRound}`);
34102
+ const reviewsDir = join14(currentRoundDir, "reviews");
33344
34103
  const reviewers = [];
33345
- if (existsSync8(reviewsDir)) {
33346
- const entries = readdirSync3(reviewsDir);
34104
+ if (existsSync12(reviewsDir)) {
34105
+ const entries = readdirSync4(reviewsDir);
33347
34106
  const reviewFiles = entries.filter((f) => f.endsWith(".md"));
33348
34107
  for (const file of reviewFiles) {
33349
- const reviewPath = join10(reviewsDir, file);
34108
+ const reviewPath = join14(reviewsDir, file);
33350
34109
  const findings = countFindings(reviewPath);
33351
34110
  reviewers.push({
33352
34111
  name: file.replace(".md", ""),
@@ -33356,14 +34115,14 @@ var ReviewProgressStrategy = class {
33356
34115
  });
33357
34116
  }
33358
34117
  }
33359
- const contextComplete = existsSync8(
33360
- join10(sessionPath, "discovered-standards.md")
34118
+ const contextComplete = existsSync12(
34119
+ join14(sessionPath, "discovered-standards.md")
33361
34120
  );
33362
- const changeContextComplete = existsSync8(join10(sessionPath, "context.md"));
34121
+ const changeContextComplete = existsSync12(join14(sessionPath, "context.md"));
33363
34122
  const analysisComplete = changeContextComplete;
33364
34123
  const reviewsComplete = state.phase_number > 4;
33365
- const discourseComplete = existsSync8(join10(currentRoundDir, "discourse.md"));
33366
- const synthesisComplete = existsSync8(join10(currentRoundDir, "final.md"));
34124
+ const discourseComplete = existsSync12(join14(currentRoundDir, "discourse.md"));
34125
+ const synthesisComplete = existsSync12(join14(currentRoundDir, "final.md"));
33367
34126
  return {
33368
34127
  workflowType: "review",
33369
34128
  session,
@@ -33495,8 +34254,8 @@ var ReviewProgressStrategy = class {
33495
34254
  var reviewStrategy = new ReviewProgressStrategy();
33496
34255
 
33497
34256
  // src/lib/progress/map-strategy.ts
33498
- import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "node:fs";
33499
- import { join as join11, basename as basename6 } from "node:path";
34257
+ import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "node:fs";
34258
+ import { join as join15, basename as basename7 } from "node:path";
33500
34259
  var MAP_PHASES = [
33501
34260
  { key: "map-context", label: "Context Discovery" },
33502
34261
  { key: "topology", label: "Topology Analysis" },
@@ -33506,23 +34265,23 @@ var MAP_PHASES = [
33506
34265
  { key: "complete", label: "Complete" }
33507
34266
  ];
33508
34267
  function deriveRunsFromFilesystem(mapDir) {
33509
- const runsDir = join11(mapDir, "runs");
33510
- if (!existsSync9(runsDir)) {
34268
+ const runsDir = join15(mapDir, "runs");
34269
+ if (!existsSync13(runsDir)) {
33511
34270
  return [];
33512
34271
  }
33513
- const runDirs = readdirSync4(runsDir).filter((d) => d.match(/^run-\d+$/)).sort((a, b) => {
34272
+ const runDirs = readdirSync5(runsDir).filter((d) => d.match(/^run-\d+$/)).sort((a, b) => {
33514
34273
  const numA = parseInt(a.replace("run-", ""));
33515
34274
  const numB = parseInt(b.replace("run-", ""));
33516
34275
  return numA - numB;
33517
34276
  });
33518
34277
  return runDirs.map((dir) => {
33519
34278
  const runNum = parseInt(dir.replace("run-", ""));
33520
- const runPath = join11(runsDir, dir);
33521
- const mapPath = join11(runPath, "map.md");
34279
+ const runPath = join15(runsDir, dir);
34280
+ const mapPath = join15(runPath, "map.md");
33522
34281
  let fileCount = 0;
33523
- const topologyPath = join11(runPath, "topology.md");
33524
- if (existsSync9(topologyPath)) {
33525
- const content = readFileSync8(topologyPath, "utf-8");
34282
+ const topologyPath = join15(runPath, "topology.md");
34283
+ if (existsSync13(topologyPath)) {
34284
+ const content = readFileSync9(topologyPath, "utf-8");
33526
34285
  const fileListMatch = content.match(
33527
34286
  /## Canonical File List[\s\S]*?```([\s\S]*?)```/
33528
34287
  );
@@ -33532,7 +34291,7 @@ function deriveRunsFromFilesystem(mapDir) {
33532
34291
  }
33533
34292
  return {
33534
34293
  run: runNum,
33535
- isComplete: existsSync9(mapPath),
34294
+ isComplete: existsSync13(mapPath),
33536
34295
  fileCount
33537
34296
  };
33538
34297
  });
@@ -33542,7 +34301,7 @@ var MapProgressStrategy = class {
33542
34301
  phases = MAP_PHASES;
33543
34302
  totalPhases = 6;
33544
34303
  parseState(sessionPath, preservedStartTime) {
33545
- const session = basename6(sessionPath);
34304
+ const session = basename7(sessionPath);
33546
34305
  const state = readSessionState(sessionPath);
33547
34306
  if (!state) {
33548
34307
  return null;
@@ -33564,24 +34323,24 @@ var MapProgressStrategy = class {
33564
34323
  parseFromState(session, state, sessionPath, preservedStartTime) {
33565
34324
  const effectiveStartTime = state.map_started_at ?? state.started_at;
33566
34325
  const startTime = preservedStartTime ?? (effectiveStartTime ? new Date(effectiveStartTime).getTime() : Date.now());
33567
- const mapDir = join11(sessionPath, "map");
34326
+ const mapDir = join15(sessionPath, "map");
33568
34327
  const runs = deriveRunsFromFilesystem(mapDir);
33569
34328
  const highestExistingRun = runs.length > 0 ? Math.max(...runs.map((r) => r.run)) : 1;
33570
34329
  const stateRun = state.current_map_run ?? 1;
33571
34330
  const currentRun = Math.min(stateRun, highestExistingRun);
33572
- const currentRunDir = join11(mapDir, "runs", `run-${currentRun}`);
33573
- const contextComplete = existsSync9(
33574
- join11(sessionPath, "discovered-standards.md")
34331
+ const currentRunDir = join15(mapDir, "runs", `run-${currentRun}`);
34332
+ const contextComplete = existsSync13(
34333
+ join15(sessionPath, "discovered-standards.md")
33575
34334
  );
33576
- const topologyComplete = existsSync9(join11(currentRunDir, "topology.md"));
33577
- const flowAnalysisComplete = existsSync9(
33578
- join11(currentRunDir, "flow-analysis.md")
34335
+ const topologyComplete = existsSync13(join15(currentRunDir, "topology.md"));
34336
+ const flowAnalysisComplete = existsSync13(
34337
+ join15(currentRunDir, "flow-analysis.md")
33579
34338
  );
33580
- const requirementsMappingComplete = existsSync9(
33581
- join11(currentRunDir, "requirements-mapping.md")
34339
+ const requirementsMappingComplete = existsSync13(
34340
+ join15(currentRunDir, "requirements-mapping.md")
33582
34341
  );
33583
- const synthesisComplete = existsSync9(join11(currentRunDir, "map.md"));
33584
- const hasRequirements = existsSync9(join11(sessionPath, "requirements.md"));
34342
+ const synthesisComplete = existsSync13(join15(currentRunDir, "map.md"));
34343
+ const hasRequirements = existsSync13(join15(sessionPath, "requirements.md"));
33585
34344
  const flowAnalysts = flowAnalysisComplete ? [
33586
34345
  {
33587
34346
  name: "flow-analyst",
@@ -34036,10 +34795,10 @@ function renderCombinedProgress(sessionPath, preservedStartTimes, ocrDir) {
34036
34795
  }
34037
34796
 
34038
34797
  // src/commands/state.ts
34039
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "node:fs";
34040
- import { join as join18 } from "node:path";
34798
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync12, readdirSync as readdirSync8 } from "node:fs";
34799
+ import { join as join19 } from "node:path";
34041
34800
 
34042
- // src/lib/state/index.ts
34801
+ // ../shared/persistence/src/state/index.ts
34043
34802
  init_db();
34044
34803
  init_exit_codes();
34045
34804
  import {
@@ -34052,7 +34811,7 @@ import {
34052
34811
  } from "node:fs";
34053
34812
  import { join as join17 } from "node:path";
34054
34813
 
34055
- // src/lib/state/phase-graph.ts
34814
+ // ../shared/persistence/src/state/phase-graph.ts
34056
34815
  init_exit_codes();
34057
34816
  var REVIEW_PHASE_NUMBERS = {
34058
34817
  context: 1,
@@ -34134,7 +34893,7 @@ function validatePhaseTransition(workflowType, source, target, isRoundBoundary)
34134
34893
  }
34135
34894
  }
34136
34895
 
34137
- // src/lib/state/meta-util.ts
34896
+ // ../shared/persistence/src/state/meta-util.ts
34138
34897
  var DEFAULT_METADATA_MAX_LEN = 4096;
34139
34898
  function sanitizeMetadataString(s, opts = {}) {
34140
34899
  const maxLen = opts.maxLen ?? DEFAULT_METADATA_MAX_LEN;
@@ -34144,9 +34903,11 @@ function sanitizeMetadataString(s, opts = {}) {
34144
34903
  return out;
34145
34904
  }
34146
34905
 
34147
- // src/lib/state/round-meta.ts
34906
+ // ../shared/persistence/src/state/round-meta.ts
34907
+ init_src();
34148
34908
  var VALID_CATEGORIES = /* @__PURE__ */ new Set(["blocker", "should_fix", "suggestion", "style"]);
34149
34909
  var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info"]);
34910
+ var MIN_TITLE_LEN = 8;
34150
34911
  function validateRoundMeta(meta) {
34151
34912
  if (!meta || typeof meta !== "object") {
34152
34913
  throw new Error("round-meta.json must be a JSON object");
@@ -34157,10 +34918,16 @@ function validateRoundMeta(meta) {
34157
34918
  `Unsupported schema_version: ${String(obj.schema_version)}. Expected 1.`
34158
34919
  );
34159
34920
  }
34160
- if (typeof obj.verdict !== "string" || obj.verdict.trim().length === 0) {
34161
- throw new Error("round-meta.json must contain a non-empty verdict string");
34921
+ if (typeof obj.verdict !== "string") {
34922
+ throw new Error("round-meta.json must contain a verdict string");
34162
34923
  }
34163
- obj.verdict = sanitizeMetadataString(obj.verdict);
34924
+ const verdict = sanitizeMetadataString(obj.verdict).trim();
34925
+ if (!isCanonicalVerdict(verdict)) {
34926
+ throw new Error(
34927
+ `round-meta.json verdict "${String(obj.verdict)}" is not one of: ${CANONICAL_VERDICTS.join(", ")}`
34928
+ );
34929
+ }
34930
+ obj.verdict = verdict;
34164
34931
  if (!Array.isArray(obj.reviewers)) {
34165
34932
  throw new Error("round-meta.json must contain a reviewers array");
34166
34933
  }
@@ -34183,8 +34950,10 @@ function validateRoundMeta(meta) {
34183
34950
  throw new Error("Each finding must be an object");
34184
34951
  }
34185
34952
  const f = finding;
34186
- if (typeof f.title !== "string" || f.title.trim().length === 0) {
34187
- throw new Error("Each finding must have a non-empty title");
34953
+ if (typeof f.title !== "string" || f.title.trim().length < MIN_TITLE_LEN) {
34954
+ throw new Error(
34955
+ `Each finding title must be at least ${MIN_TITLE_LEN} characters; got "${String(f.title)}"`
34956
+ );
34188
34957
  }
34189
34958
  f.title = sanitizeMetadataString(f.title);
34190
34959
  if (typeof f.category !== "string" || !VALID_CATEGORIES.has(f.category)) {
@@ -34229,25 +34998,144 @@ function validateRoundMeta(meta) {
34229
34998
  if (typeof sc.suggestions !== "number" || sc.suggestions < 0) {
34230
34999
  throw new Error("synthesis_counts.suggestions must be a non-negative number");
34231
35000
  }
35001
+ const allFindings = obj.reviewers.flatMap((reviewer) => reviewer.findings);
35002
+ const derived = deriveCounts(allFindings);
35003
+ if (sc.blockers > derived.blocker) {
35004
+ throw new Error(
35005
+ `synthesis_counts.blockers (${sc.blockers}) exceeds the ${derived.blocker} blocker finding(s) present`
35006
+ );
35007
+ }
35008
+ if (sc.should_fix > derived.should_fix) {
35009
+ throw new Error(
35010
+ `synthesis_counts.should_fix (${sc.should_fix}) exceeds the ${derived.should_fix} should_fix finding(s) present`
35011
+ );
35012
+ }
35013
+ if (sc.suggestions > derived.suggestion) {
35014
+ throw new Error(
35015
+ `synthesis_counts.suggestions (${sc.suggestions}) exceeds the ${derived.suggestion} suggestion finding(s) present`
35016
+ );
35017
+ }
35018
+ }
35019
+ const { blockerCount } = resolveRoundCounts(obj);
35020
+ if (verdict === "APPROVE" && blockerCount > 0) {
35021
+ throw new Error(
35022
+ `round-meta.json verdict "APPROVE" is inconsistent with ${blockerCount} blocker finding(s); APPROVE requires zero blockers (use "REQUEST CHANGES", or carry residual work as should_fix/suggestion/style)`
35023
+ );
35024
+ }
35025
+ if (verdict === "REQUEST CHANGES" && blockerCount === 0) {
35026
+ throw new Error(
35027
+ `round-meta.json verdict "REQUEST CHANGES" requires at least one blocker finding; found ${blockerCount} (use "APPROVE" if there is nothing to block on, or "NEEDS DISCUSSION")`
35028
+ );
34232
35029
  }
34233
35030
  return meta;
34234
35031
  }
34235
35032
  function computeRoundCounts(meta) {
34236
- const allFindings = [];
34237
- for (const reviewer of meta.reviewers) {
34238
- allFindings.push(...reviewer.findings);
35033
+ return resolveRoundCounts(meta);
35034
+ }
35035
+
35036
+ // ../shared/persistence/src/state/forward-resume.ts
35037
+ init_db();
35038
+ init_liveness();
35039
+ var FORWARD_RESUME_KIND = "forward_resume";
35040
+ var FORWARD_RESUME_EXHAUSTED_REASON = "forward_resume_exhausted";
35041
+ function parseLeaseMetadata(e) {
35042
+ if (e.event_type !== "session_resumed" || !e.metadata) return null;
35043
+ try {
35044
+ return JSON.parse(e.metadata);
35045
+ } catch {
35046
+ return null;
35047
+ }
35048
+ }
35049
+ function remainingPhasesAfter(workflowType, currentPhase) {
35050
+ const numbers = workflowType === "map" ? MAP_PHASE_NUMBERS : REVIEW_PHASE_NUMBERS;
35051
+ const cur = numbers[currentPhase];
35052
+ if (cur === void 0) return [];
35053
+ return Object.entries(numbers).filter(([, n]) => n > cur).sort((a, b) => a[1] - b[1]).map(([phase]) => phase);
35054
+ }
35055
+ function countForwardResumeLeases(events, round) {
35056
+ return events.filter((e) => parseLeaseMetadata(e)?.kind === FORWARD_RESUME_KIND && parseLeaseMetadata(e)?.round === round).length;
35057
+ }
35058
+ function forwardResumeLeaseState(events, round, leaseMs, nowMs) {
35059
+ const leases = events.filter(
35060
+ (e) => parseLeaseMetadata(e)?.kind === FORWARD_RESUME_KIND && parseLeaseMetadata(e)?.round === round
35061
+ );
35062
+ if (leases.length === 0) return { leaseCount: 0, activeLeaseHeld: false };
35063
+ const latestLease = leases[leases.length - 1];
35064
+ const latestLeaseMs = sqliteUtcMs(latestLease.created_at);
35065
+ let effectiveMs = latestLeaseMs;
35066
+ for (const e of events) {
35067
+ if (e.event_type === "phase_transition" && (e.round == null || e.round === round)) {
35068
+ const t = sqliteUtcMs(e.created_at);
35069
+ if (t >= latestLeaseMs && t > effectiveMs) effectiveMs = t;
35070
+ }
34239
35071
  }
34240
- const sc = meta.synthesis_counts;
34241
35072
  return {
34242
- blockerCount: sc ? sc.blockers : allFindings.filter((f) => f.category === "blocker").length,
34243
- shouldFixCount: sc ? sc.should_fix : allFindings.filter((f) => f.category === "should_fix").length,
34244
- suggestionCount: sc ? sc.suggestions : allFindings.filter((f) => f.category === "suggestion").length,
34245
- reviewerCount: meta.reviewers.length,
34246
- totalFindingCount: allFindings.length
35073
+ leaseCount: leases.length,
35074
+ activeLeaseHeld: nowMs - effectiveMs < leaseMs
35075
+ };
35076
+ }
35077
+ function tryAcquireForwardResumeLease(db, sessionId, round, opts) {
35078
+ const nowMs = opts.nowMs ?? Date.now();
35079
+ return db.transaction(() => {
35080
+ const events = getEventsForSession(db, sessionId);
35081
+ const { leaseCount, activeLeaseHeld } = forwardResumeLeaseState(
35082
+ events,
35083
+ round,
35084
+ opts.leaseMs,
35085
+ nowMs
35086
+ );
35087
+ if (leaseCount >= opts.maxAttempts) {
35088
+ return { acquired: false, reason: "cap_exhausted", attemptsUsed: leaseCount };
35089
+ }
35090
+ if (activeLeaseHeld) {
35091
+ return { acquired: false, reason: "lease_held", attemptsUsed: leaseCount };
35092
+ }
35093
+ insertEvent(db, {
35094
+ session_id: sessionId,
35095
+ event_type: "session_resumed",
35096
+ metadata: JSON.stringify({ kind: FORWARD_RESUME_KIND, round })
35097
+ });
35098
+ return { acquired: true, attemptsUsed: leaseCount + 1 };
35099
+ });
35100
+ }
35101
+ function hasLiveOwningTurn(db, sessionId, heartbeatMs, nowMs) {
35102
+ const instances = listAgentSessionsForWorkflow(db, sessionId);
35103
+ return instances.some(
35104
+ (s) => s.ended_at == null && nowMs - sqliteUtcMs(s.last_heartbeat_at) <= heartbeatMs
35105
+ );
35106
+ }
35107
+ function deriveStrandedStatus(db, session, cfg) {
35108
+ const nowMs = cfg.nowMs ?? Date.now();
35109
+ if (hasLiveOwningTurn(db, session.id, cfg.heartbeatMs, nowMs)) return null;
35110
+ return strandedActionByCap(db, session, cfg.maxAttempts);
35111
+ }
35112
+ function strandedActionByCap(db, session, maxAttempts) {
35113
+ const events = getEventsForSession(db, session.id);
35114
+ const leaseCount = countForwardResumeLeases(events, session.current_round);
35115
+ const workflowType = session.workflow_type === "map" ? "map" : "review";
35116
+ return {
35117
+ action: leaseCount >= maxAttempts ? "abort_or_fresh" : "forward_resume",
35118
+ remainingPhases: remainingPhasesAfter(workflowType, session.current_phase),
35119
+ attemptsRemaining: Math.max(0, maxAttempts - leaseCount)
34247
35120
  };
34248
35121
  }
35122
+ function closeForwardResumeExhausted(db, sessionId, attempts) {
35123
+ commitReasonClose(
35124
+ db,
35125
+ sessionId,
35126
+ {
35127
+ event_type: "session_auto_closed_stale",
35128
+ phase: "complete",
35129
+ metadata: JSON.stringify({
35130
+ reason: FORWARD_RESUME_EXHAUSTED_REASON,
35131
+ attempts
35132
+ })
35133
+ },
35134
+ { status: "closed", current_phase: "complete" }
35135
+ );
35136
+ }
34249
35137
 
34250
- // src/lib/state/map-meta.ts
35138
+ // ../shared/persistence/src/state/map-meta.ts
34251
35139
  function validateMapMeta(meta) {
34252
35140
  if (!meta || typeof meta !== "object") {
34253
35141
  throw new Error("map-meta.json must be a JSON object");
@@ -34314,7 +35202,7 @@ function computeMapCounts(meta) {
34314
35202
  };
34315
35203
  }
34316
35204
 
34317
- // src/lib/state/projection.ts
35205
+ // ../shared/persistence/src/state/projection.ts
34318
35206
  init_db();
34319
35207
  var REASON_EVENT_TYPES = [
34320
35208
  "session_aborted",
@@ -34344,7 +35232,7 @@ function getCompletenessState(db, sessionId) {
34344
35232
  return r[0]?.values[0]?.[0] ?? null;
34345
35233
  }
34346
35234
 
34347
- // src/lib/state/index.ts
35235
+ // ../shared/persistence/src/state/index.ts
34348
35236
  init_exit_codes();
34349
35237
  function deriveNextRound(db, sessionId, fallbackRound) {
34350
35238
  const result = db.exec(
@@ -34399,6 +35287,12 @@ async function stateInit(params) {
34399
35287
  `Cannot re-open session ${sessionId} as workflow_type "${workflowType}": existing workflow_type is "${existing.workflow_type}". Maps and reviews have disjoint phase graphs.`
34400
35288
  );
34401
35289
  }
35290
+ if (existing.status === "active" && !hasCompletionInvariant(db, existing)) {
35291
+ throw new StateError(
35292
+ STATE_EXIT.INVARIANT_UNMET,
35293
+ `Session ${sessionId} is active and its current round is not complete \u2014 'begin' would reset it to "${initialPhaseFor(workflowType)}" and lose progress. Forward-resume instead: re-run the review (it continues from current_phase via 'ocr state status --json'), or 'ocr review --resume ${sessionId}'.`
35294
+ );
35295
+ }
34402
35296
  const nextRound = deriveNextRound(db, sessionId, existing.current_round);
34403
35297
  const initialPhase2 = workflowType === "map" ? "map-context" : "context";
34404
35298
  db.transaction(() => {
@@ -34698,18 +35592,19 @@ async function stateCompleteRound(params) {
34698
35592
  }
34699
35593
  const resolved = resolveSession(db, params.sessionId);
34700
35594
  const roundNumber = params.round ?? resolved.current_round;
34701
- const roundMetaPath = join17(
34702
- resolved.session_dir,
34703
- "rounds",
34704
- `round-${roundNumber}`,
34705
- "round-meta.json"
34706
- );
35595
+ const roundDir = join17(resolved.session_dir, "rounds", `round-${roundNumber}`);
35596
+ const roundMetaPath = join17(roundDir, "round-meta.json");
35597
+ const materializeArtifact = () => {
35598
+ mkdirSync6(roundDir, { recursive: true });
35599
+ writeFileSync7(roundMetaPath, JSON.stringify(meta, null, 2));
35600
+ };
34707
35601
  const already = db.exec(
34708
35602
  `SELECT 1 FROM orchestration_events
34709
35603
  WHERE session_id = ? AND event_type = 'round_completed' AND round = ? LIMIT 1`,
34710
35604
  [resolved.id, roundNumber]
34711
35605
  );
34712
35606
  if ((already[0]?.values.length ?? 0) > 0) {
35607
+ if (!existsSync15(roundMetaPath)) materializeArtifact();
34713
35608
  return { sessionId: resolved.id, round: roundNumber, metaPath: roundMetaPath, schema_version: 1 };
34714
35609
  }
34715
35610
  if (resolved.current_phase !== "synthesis") {
@@ -34719,7 +35614,7 @@ async function stateCompleteRound(params) {
34719
35614
  );
34720
35615
  }
34721
35616
  if (params.requireFinal) {
34722
- const finalPath = join17(resolved.session_dir, "rounds", `round-${roundNumber}`, "final.md");
35617
+ const finalPath = join17(roundDir, "final.md");
34723
35618
  if (!existsSync15(finalPath)) {
34724
35619
  throw new StateError(
34725
35620
  STATE_EXIT.INVARIANT_UNMET,
@@ -34727,13 +35622,8 @@ async function stateCompleteRound(params) {
34727
35622
  );
34728
35623
  }
34729
35624
  }
34730
- let metaPath;
34731
- if (params.source === "stdin") {
34732
- const roundDir = join17(resolved.session_dir, "rounds", `round-${roundNumber}`);
34733
- mkdirSync6(roundDir, { recursive: true });
34734
- metaPath = roundMetaPath;
34735
- writeFileSync7(metaPath, JSON.stringify(meta, null, 2));
34736
- }
35625
+ materializeArtifact();
35626
+ const metaPath = roundMetaPath;
34737
35627
  db.transaction(() => {
34738
35628
  insertEvent(db, {
34739
35629
  session_id: resolved.id,
@@ -34784,19 +35674,19 @@ async function stateCompleteMap(params) {
34784
35674
  }
34785
35675
  const resolved = resolveSession(db, params.sessionId);
34786
35676
  const mapRunNumber = params.mapRun ?? resolved.current_map_run;
34787
- const mapMetaPath = join17(
34788
- resolved.session_dir,
34789
- "map",
34790
- "runs",
34791
- `run-${mapRunNumber}`,
34792
- "map-meta.json"
34793
- );
35677
+ const runDir = join17(resolved.session_dir, "map", "runs", `run-${mapRunNumber}`);
35678
+ const mapMetaPath = join17(runDir, "map-meta.json");
35679
+ const materializeArtifact = () => {
35680
+ mkdirSync6(runDir, { recursive: true });
35681
+ writeFileSync7(mapMetaPath, JSON.stringify(meta, null, 2));
35682
+ };
34794
35683
  const already = db.exec(
34795
35684
  `SELECT 1 FROM orchestration_events
34796
35685
  WHERE session_id = ? AND event_type = 'map_completed' AND round = ? LIMIT 1`,
34797
35686
  [resolved.id, mapRunNumber]
34798
35687
  );
34799
35688
  if ((already[0]?.values.length ?? 0) > 0) {
35689
+ if (!existsSync15(mapMetaPath)) materializeArtifact();
34800
35690
  return { sessionId: resolved.id, mapRun: mapRunNumber, metaPath: mapMetaPath, schema_version: 1 };
34801
35691
  }
34802
35692
  if (resolved.current_phase !== "synthesis") {
@@ -34805,13 +35695,8 @@ async function stateCompleteMap(params) {
34805
35695
  `Cannot complete map: workflow is at "${resolved.current_phase}", not "synthesis". Advance first.`
34806
35696
  );
34807
35697
  }
34808
- let metaPath;
34809
- if (params.source === "stdin") {
34810
- const runDir = join17(resolved.session_dir, "map", "runs", `run-${mapRunNumber}`);
34811
- mkdirSync6(runDir, { recursive: true });
34812
- metaPath = mapMetaPath;
34813
- writeFileSync7(metaPath, JSON.stringify(meta, null, 2));
34814
- }
35698
+ materializeArtifact();
35699
+ const metaPath = mapMetaPath;
34815
35700
  db.transaction(() => {
34816
35701
  insertEvent(db, {
34817
35702
  session_id: resolved.id,
@@ -34837,7 +35722,7 @@ async function stateCompleteMap(params) {
34837
35722
  });
34838
35723
  return { sessionId: resolved.id, mapRun: mapRunNumber, metaPath, schema_version: 1 };
34839
35724
  }
34840
- async function stateStatus(ocrDir, sessionId) {
35725
+ async function stateStatus(ocrDir, sessionId, forwardResume) {
34841
35726
  const db = await ensureDatabase(ocrDir);
34842
35727
  const resolved = resolveSession(db, sessionId);
34843
35728
  const view = db.exec(
@@ -34850,6 +35735,8 @@ async function stateStatus(ocrDir, sessionId) {
34850
35735
  const hasTerminalArtifact = row?.[1] === 1;
34851
35736
  let nextAction;
34852
35737
  let nextActionKind;
35738
+ let remainingPhases;
35739
+ let attemptsRemaining;
34853
35740
  switch (completenessState) {
34854
35741
  case "complete":
34855
35742
  nextAction = "none \u2014 session is complete";
@@ -34867,12 +35754,25 @@ async function stateStatus(ocrDir, sessionId) {
34867
35754
  if (hasTerminalArtifact) {
34868
35755
  nextAction = "run 'ocr state finish' to close the workflow";
34869
35756
  nextActionKind = "finish";
34870
- } else if (resolved.current_phase === "synthesis") {
34871
- nextAction = "pipe round metadata to 'ocr state complete-round --stdin'";
34872
- nextActionKind = "complete_round";
34873
35757
  } else {
34874
- nextAction = "advance through the phases, then 'ocr state complete-round'";
34875
- nextActionKind = "advance";
35758
+ const stranded = forwardResume && resolved.status === "active" ? deriveStrandedStatus(db, resolved, forwardResume) : null;
35759
+ if (stranded) {
35760
+ remainingPhases = stranded.remainingPhases;
35761
+ attemptsRemaining = stranded.attemptsRemaining;
35762
+ if (stranded.action === "forward_resume") {
35763
+ nextAction = `forward-resume from '${resolved.current_phase}': re-run the review (it continues via 'ocr state status --json'), or 'ocr review --resume ${resolved.id}'`;
35764
+ nextActionKind = "forward_resume";
35765
+ } else {
35766
+ nextAction = "forward-resume attempts exhausted \u2014 abort with 'ocr state finish --abort' or start a fresh review";
35767
+ nextActionKind = "abort_or_fresh";
35768
+ }
35769
+ } else if (resolved.current_phase === "synthesis") {
35770
+ nextAction = "pipe round metadata to 'ocr state complete-round --stdin'";
35771
+ nextActionKind = "complete_round";
35772
+ } else {
35773
+ nextAction = "advance through the phases, then 'ocr state complete-round'";
35774
+ nextActionKind = "advance";
35775
+ }
34876
35776
  }
34877
35777
  }
34878
35778
  return {
@@ -34888,7 +35788,9 @@ async function stateStatus(ocrDir, sessionId) {
34888
35788
  marked_closed: row?.[2] === 1,
34889
35789
  dependents_settled: row?.[3] === 1,
34890
35790
  next_action: nextAction,
34891
- next_action_kind: nextActionKind
35791
+ next_action_kind: nextActionKind,
35792
+ ...remainingPhases ? { remaining_phases: remainingPhases } : {},
35793
+ ...attemptsRemaining !== void 0 ? { forward_resume_attempts_remaining: attemptsRemaining } : {}
34892
35794
  };
34893
35795
  }
34894
35796
  async function stateSync(ocrDir) {
@@ -34976,14 +35878,73 @@ async function stateSync(ocrDir) {
34976
35878
  }
34977
35879
 
34978
35880
  // src/commands/state.ts
34979
- init_command_log();
34980
35881
  init_db();
34981
35882
  init_db();
34982
- function readDashboardSpawnMarker(ocrDir) {
34983
- const path2 = join18(ocrDir, "data", "dashboard-active-spawn.json");
35883
+
35884
+ // ../shared/config/src/runtime-config.ts
35885
+ import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
35886
+ import { join as join18 } from "node:path";
35887
+ var DEFAULT_AGENT_HEARTBEAT_SECONDS = 60;
35888
+ var DEFAULT_FORWARD_RESUME_MAX_ATTEMPTS = 2;
35889
+ var DEFAULT_FORWARD_RESUME_LEASE_SECONDS = 1800;
35890
+ function readRuntimePositiveInt(ocrDir, key, defaultValue) {
35891
+ const configPath = join18(ocrDir, "config.yaml");
35892
+ if (!existsSync16(configPath)) return defaultValue;
35893
+ let content;
35894
+ try {
35895
+ content = readFileSync11(configPath, "utf-8");
35896
+ } catch {
35897
+ return defaultValue;
35898
+ }
35899
+ const blockMatch = content.match(
35900
+ new RegExp(
35901
+ String.raw`^runtime:\s*\n(?:\s+[^\n]*\n)*?\s+${key}:\s*([^\s#\n]+)`,
35902
+ "m"
35903
+ )
35904
+ );
35905
+ const inlineMatch = content.match(
35906
+ new RegExp(String.raw`^runtime:\s*\{[^}]*\b${key}:\s*([^\s,}]+)`, "m")
35907
+ );
35908
+ const raw = blockMatch?.[1] ?? inlineMatch?.[1];
35909
+ if (!raw) return defaultValue;
35910
+ const parsed = Number(raw);
35911
+ if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
35912
+ process.stderr.write(
35913
+ `[ocr] runtime.${key} is not a positive integer (got "${raw}"); falling back to ${defaultValue}.
35914
+ `
35915
+ );
35916
+ return defaultValue;
35917
+ }
35918
+ return parsed;
35919
+ }
35920
+ function getAgentHeartbeatSeconds(ocrDir) {
35921
+ return readRuntimePositiveInt(
35922
+ ocrDir,
35923
+ "agent_heartbeat_seconds",
35924
+ DEFAULT_AGENT_HEARTBEAT_SECONDS
35925
+ );
35926
+ }
35927
+ function getForwardResumeMaxAttempts(ocrDir) {
35928
+ return readRuntimePositiveInt(
35929
+ ocrDir,
35930
+ "forward_resume_max_attempts",
35931
+ DEFAULT_FORWARD_RESUME_MAX_ATTEMPTS
35932
+ );
35933
+ }
35934
+ function getForwardResumeLeaseMs(ocrDir) {
35935
+ return readRuntimePositiveInt(
35936
+ ocrDir,
35937
+ "forward_resume_lease_seconds",
35938
+ DEFAULT_FORWARD_RESUME_LEASE_SECONDS
35939
+ ) * 1e3;
35940
+ }
35941
+
35942
+ // src/commands/state.ts
35943
+ init_db();
35944
+ function readMarkerFile(path2) {
34984
35945
  let raw;
34985
35946
  try {
34986
- raw = readFileSync11(path2, "utf-8");
35947
+ raw = readFileSync12(path2, "utf-8");
34987
35948
  } catch {
34988
35949
  return null;
34989
35950
  }
@@ -35004,6 +35965,30 @@ function readDashboardSpawnMarker(ocrDir) {
35004
35965
  }
35005
35966
  return marker;
35006
35967
  }
35968
+ function readDashboardSpawnMarker(ocrDir) {
35969
+ const dir = join19(ocrDir, "data", "dashboard-active-spawn");
35970
+ let entries = [];
35971
+ try {
35972
+ entries = readdirSync8(dir).filter((f) => f.endsWith(".json"));
35973
+ } catch {
35974
+ entries = [];
35975
+ }
35976
+ const live = [];
35977
+ for (const entry of entries) {
35978
+ const marker = readMarkerFile(join19(dir, entry));
35979
+ if (marker) live.push(marker);
35980
+ }
35981
+ if (live.length === 1) return live[0] ?? null;
35982
+ if (live.length > 1) {
35983
+ console.error(
35984
+ source_default.gray(
35985
+ `[state] ${live.length} concurrent dashboard spawns live; marker fallback is ambiguous \u2014 pass --dashboard-uid for linkage`
35986
+ )
35987
+ );
35988
+ return null;
35989
+ }
35990
+ return readMarkerFile(join19(ocrDir, "data", "dashboard-active-spawn.json"));
35991
+ }
35007
35992
  async function readStdin() {
35008
35993
  const chunks = [];
35009
35994
  for await (const chunk of process.stdin) {
@@ -35045,7 +36030,7 @@ async function linkDashboardInvocation(ocrDir, sessionId, explicitUid, label) {
35045
36030
  var showSubcommand = new Command("show").description("Show current session state").option("--session-id <id>", "Session ID (defaults to latest active)").option("--json", "Output as JSON").action(async (options) => {
35046
36031
  const targetDir = process.cwd();
35047
36032
  requireOcrSetup(targetDir);
35048
- const ocrDir = join18(targetDir, ".ocr");
36033
+ const ocrDir = join19(targetDir, ".ocr");
35049
36034
  try {
35050
36035
  const result = await stateShow(ocrDir, options.sessionId);
35051
36036
  if (!result) {
@@ -35114,7 +36099,7 @@ var showSubcommand = new Command("show").description("Show current session state
35114
36099
  var syncSubcommand = new Command("sync").description("Rebuild session state from filesystem artifacts").action(async () => {
35115
36100
  const targetDir = process.cwd();
35116
36101
  requireOcrSetup(targetDir);
35117
- const ocrDir = join18(targetDir, ".ocr");
36102
+ const ocrDir = join19(targetDir, ".ocr");
35118
36103
  try {
35119
36104
  const synced = await stateSync(ocrDir);
35120
36105
  console.log(`Synced ${synced} session${synced !== 1 ? "s" : ""} from filesystem.`);
@@ -35141,7 +36126,7 @@ var reconcileSubcommand = new Command("reconcile").description(
35141
36126
  ).option("--dry-run", "Print the repair plan without writing anything").option("--json", "Output the result as JSON").action(async (options) => {
35142
36127
  const targetDir = process.cwd();
35143
36128
  requireOcrSetup(targetDir);
35144
- const ocrDir = join18(targetDir, ".ocr");
36129
+ const ocrDir = join19(targetDir, ".ocr");
35145
36130
  try {
35146
36131
  const db = await ensureDatabase(ocrDir);
35147
36132
  const result = reconcileLegacyState(db, ocrDir, { dryRun: options.dryRun });
@@ -35200,9 +36185,9 @@ var beginSubcommand = new Command("begin").description("Start or resume a workfl
35200
36185
  async (options) => {
35201
36186
  const targetDir = process.cwd();
35202
36187
  requireOcrSetup(targetDir);
35203
- const ocrDir = join18(targetDir, ".ocr");
35204
- const sessionDir = options.sessionDir ?? join18(ocrDir, "sessions", options.sessionId);
35205
- if (!existsSync16(sessionDir)) mkdirSync7(sessionDir, { recursive: true });
36188
+ const ocrDir = join19(targetDir, ".ocr");
36189
+ const sessionDir = options.sessionDir ?? join19(ocrDir, "sessions", options.sessionId);
36190
+ if (!existsSync17(sessionDir)) mkdirSync7(sessionDir, { recursive: true });
35206
36191
  try {
35207
36192
  const result = await stateBegin({
35208
36193
  sessionId: options.sessionId,
@@ -35224,7 +36209,7 @@ var advanceSubcommand = new Command("advance").description("Advance the workflow
35224
36209
  async (options) => {
35225
36210
  const targetDir = process.cwd();
35226
36211
  requireOcrSetup(targetDir);
35227
- const ocrDir = join18(targetDir, ".ocr");
36212
+ const ocrDir = join19(targetDir, ".ocr");
35228
36213
  try {
35229
36214
  const { id: sessionId } = await resolveActiveSession(ocrDir, options.sessionId);
35230
36215
  await stateAdvance({
@@ -35244,7 +36229,7 @@ var completeRoundSubcommand = new Command("complete-round").description("Atomica
35244
36229
  async (options) => {
35245
36230
  const targetDir = process.cwd();
35246
36231
  requireOcrSetup(targetDir);
35247
- const ocrDir = join18(targetDir, ".ocr");
36232
+ const ocrDir = join19(targetDir, ".ocr");
35248
36233
  try {
35249
36234
  const base = options.stdin ? { source: "stdin", data: await readStdin() } : options.file ? { source: "file", filePath: options.file } : (() => {
35250
36235
  throw new StateError(STATE_EXIT.USAGE, "Provide --stdin or --file with round metadata");
@@ -35268,7 +36253,7 @@ var completeMapSubcommand = new Command("complete-map").description("Atomically
35268
36253
  async (options) => {
35269
36254
  const targetDir = process.cwd();
35270
36255
  requireOcrSetup(targetDir);
35271
- const ocrDir = join18(targetDir, ".ocr");
36256
+ const ocrDir = join19(targetDir, ".ocr");
35272
36257
  try {
35273
36258
  const base = options.stdin ? { source: "stdin", data: await readStdin() } : options.file ? { source: "file", filePath: options.file } : (() => {
35274
36259
  throw new StateError(STATE_EXIT.USAGE, "Provide --stdin or --file with map metadata");
@@ -35290,7 +36275,7 @@ var completeMapSubcommand = new Command("complete-map").description("Atomically
35290
36275
  var finishSubcommand = new Command("finish").description("Close a workflow (refuses unless the current round/run is complete)").option("--session-id <id>", "Session ID (auto-detects active if omitted)").option("--abort", "Abandon the session \u2014 records a distinct, non-success terminal").action(async (options) => {
35291
36276
  const targetDir = process.cwd();
35292
36277
  requireOcrSetup(targetDir);
35293
- const ocrDir = join18(targetDir, ".ocr");
36278
+ const ocrDir = join19(targetDir, ".ocr");
35294
36279
  try {
35295
36280
  const { id: sessionId } = await resolveActiveSession(ocrDir, options.sessionId);
35296
36281
  await stateClose({ sessionId, ocrDir, abort: options.abort });
@@ -35302,14 +36287,20 @@ var finishSubcommand = new Command("finish").description("Close a workflow (refu
35302
36287
  var statusSubcommand = new Command("status").description("Report whether a session is complete and, if not, what's missing").option("--session-id <id>", "Session ID (auto-detects active if omitted)").option("--json", "Output the result as JSON").action(async (options) => {
35303
36288
  const targetDir = process.cwd();
35304
36289
  requireOcrSetup(targetDir);
35305
- const ocrDir = join18(targetDir, ".ocr");
36290
+ const ocrDir = join19(targetDir, ".ocr");
35306
36291
  try {
35307
- const result = await stateStatus(ocrDir, options.sessionId);
36292
+ const result = await stateStatus(ocrDir, options.sessionId, {
36293
+ maxAttempts: getForwardResumeMaxAttempts(ocrDir),
36294
+ heartbeatMs: getAgentHeartbeatSeconds(ocrDir) * 1e3
36295
+ });
35308
36296
  if (options.json) {
35309
36297
  console.log(JSON.stringify(result, null, 2));
35310
36298
  } else {
35311
36299
  console.log(`${result.session_id}: ${result.completeness_state}`);
35312
36300
  console.log(source_default.dim(` next: ${result.next_action}`));
36301
+ if (result.remaining_phases?.length) {
36302
+ console.log(source_default.dim(` remaining: ${result.remaining_phases.join(" \u2192 ")}`));
36303
+ }
35313
36304
  }
35314
36305
  } catch (error) {
35315
36306
  exitFromStateError(error, "Failed to read status");
@@ -35333,50 +36324,6 @@ var stateCommand = new Command("state").description("Manage OCR session state").
35333
36324
  import { randomUUID as randomUUID3 } from "node:crypto";
35334
36325
  import { join as join20 } from "node:path";
35335
36326
  init_db();
35336
-
35337
- // src/lib/runtime-config.ts
35338
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "node:fs";
35339
- import { join as join19 } from "node:path";
35340
- var DEFAULT_AGENT_HEARTBEAT_SECONDS = 60;
35341
- function readRuntimePositiveInt(ocrDir, key, defaultValue) {
35342
- const configPath = join19(ocrDir, "config.yaml");
35343
- if (!existsSync17(configPath)) return defaultValue;
35344
- let content;
35345
- try {
35346
- content = readFileSync12(configPath, "utf-8");
35347
- } catch {
35348
- return defaultValue;
35349
- }
35350
- const blockMatch = content.match(
35351
- new RegExp(
35352
- String.raw`^runtime:\s*\n(?:\s+[^\n]*\n)*?\s+${key}:\s*([^\s#\n]+)`,
35353
- "m"
35354
- )
35355
- );
35356
- const inlineMatch = content.match(
35357
- new RegExp(String.raw`^runtime:\s*\{[^}]*\b${key}:\s*([^\s,}]+)`, "m")
35358
- );
35359
- const raw = blockMatch?.[1] ?? inlineMatch?.[1];
35360
- if (!raw) return defaultValue;
35361
- const parsed = Number(raw);
35362
- if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
35363
- process.stderr.write(
35364
- `[ocr] runtime.${key} is not a positive integer (got "${raw}"); falling back to ${defaultValue}.
35365
- `
35366
- );
35367
- return defaultValue;
35368
- }
35369
- return parsed;
35370
- }
35371
- function getAgentHeartbeatSeconds(ocrDir) {
35372
- return readRuntimePositiveInt(
35373
- ocrDir,
35374
- "agent_heartbeat_seconds",
35375
- DEFAULT_AGENT_HEARTBEAT_SECONDS
35376
- );
35377
- }
35378
-
35379
- // src/commands/session.ts
35380
36327
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
35381
36328
  "done",
35382
36329
  "crashed",
@@ -35427,6 +36374,11 @@ var startInstanceSubcommand = new Command("start-instance").description("Journal
35427
36374
  }
35428
36375
  );
35429
36376
  var bindVendorIdSubcommand = new Command("bind-vendor-id").description("Bind the underlying CLI's session id to an OCR agent session").argument("<agent-session-id>", "OCR agent session id").argument("<vendor-session-id>", "Underlying CLI's session id").action(async (agentId, vendorId) => {
36377
+ if (!SAFE_VENDOR_SESSION_ID.test(vendorId)) {
36378
+ fail(
36379
+ `vendor-session-id ${JSON.stringify(vendorId)} is not a plausible vendor session id (allowed: letters and digits plus . _ : - , max 256 chars). Nothing was bound \u2014 retry with the id the vendor CLI actually emitted.`
36380
+ );
36381
+ }
35430
36382
  const { ocrDir } = await setup();
35431
36383
  const db = await ensureDatabase(ocrDir);
35432
36384
  try {
@@ -35524,7 +36476,7 @@ var listSubcommand = new Command("list").description("List agent sessions for a
35524
36476
  });
35525
36477
  var sessionCommand = new Command("session").description("Manage agent-CLI session lifecycle journal").addCommand(startInstanceSubcommand).addCommand(bindVendorIdSubcommand).addCommand(beatSubcommand).addCommand(endInstanceSubcommand).addCommand(listSubcommand);
35526
36478
 
35527
- // src/lib/models.ts
36479
+ // ../shared/config/src/models.ts
35528
36480
  init_src();
35529
36481
  function parseOpenCodeModelList(stdout) {
35530
36482
  const models = [];
@@ -35770,6 +36722,9 @@ function parseSessionOverride(raw) {
35770
36722
  return result;
35771
36723
  }
35772
36724
  var resolveSubcommand = new Command("resolve").description("Resolve and print the team composition for the active workspace").option(
36725
+ "--team <spec>",
36726
+ "Session team override replacing default_team. Format: reviewer-id:count[,reviewer-id:count...] (e.g. principal:2,architect:1)"
36727
+ ).option(
35773
36728
  "--session-override <json>",
35774
36729
  "JSON array of ReviewerInstance overrides applied on top of disk config"
35775
36730
  ).option("--session-override-stdin", "Read --session-override JSON from stdin").option("--json", "Emit JSON for programmatic consumption (the AI workflow uses this)").action(
@@ -35778,7 +36733,8 @@ var resolveSubcommand = new Command("resolve").description("Resolve and print th
35778
36733
  requireOcrSetup(targetDir);
35779
36734
  const ocrDir = join21(targetDir, ".ocr");
35780
36735
  try {
35781
- const { team } = loadTeamConfig(ocrDir);
36736
+ const { team, aliases, defaultModel } = loadTeamConfig(ocrDir);
36737
+ const baseTeam = options.team ? parseTeamSpec(options.team, aliases, defaultModel) : team;
35782
36738
  let override;
35783
36739
  if (options.sessionOverride) {
35784
36740
  override = parseSessionOverride(options.sessionOverride);
@@ -35788,7 +36744,7 @@ var resolveSubcommand = new Command("resolve").description("Resolve and print th
35788
36744
  override = parseSessionOverride(raw);
35789
36745
  }
35790
36746
  }
35791
- const resolved = resolveTeamComposition(team, override);
36747
+ const resolved = resolveTeamComposition(baseTeam, override);
35792
36748
  if (options.json) {
35793
36749
  console.log(JSON.stringify(resolved, null, 2));
35794
36750
  return;
@@ -35926,11 +36882,11 @@ function pairKey(pair) {
35926
36882
  var teamCommand = new Command("team").description("Resolve and persist team composition").addCommand(resolveSubcommand).addCommand(setSubcommand);
35927
36883
 
35928
36884
  // src/commands/review.ts
35929
- import { spawn as spawn3 } from "node:child_process";
36885
+ init_src();
35930
36886
  import { join as join22 } from "node:path";
35931
36887
  init_db();
35932
36888
 
35933
- // src/lib/vendor-resume.ts
36889
+ // ../shared/persistence/src/vendor-resume.ts
35934
36890
  var VENDOR_BINARIES = {
35935
36891
  claude: "claude",
35936
36892
  opencode: "opencode"
@@ -35952,6 +36908,7 @@ function fail3(message) {
35952
36908
  console.error(source_default.red(`Error: ${message}`));
35953
36909
  process.exit(1);
35954
36910
  }
36911
+ var CONTROL_PROMPT = "Resume this OCR review: run `ocr state status --json` and act on `next_action`, continuing forward from `current_phase` without redoing completed phases.";
35955
36912
  var reviewCommand = new Command("review").description("Run or resume an OCR review").option("--resume <workflow-id>", "Resume a prior review by its workflow session id").action(async (options) => {
35956
36913
  if (!options.resume) {
35957
36914
  console.error(
@@ -35968,21 +36925,89 @@ var reviewCommand = new Command("review").description("Run or resume an OCR revi
35968
36925
  requireOcrSetup(targetDir);
35969
36926
  const ocrDir = join22(targetDir, ".ocr");
35970
36927
  const db = await ensureDatabase(ocrDir);
35971
- const session = getSession(db, options.resume);
36928
+ const workflowId = options.resume;
36929
+ const session = getSession(db, workflowId);
35972
36930
  if (!session) {
35973
- fail3(`Workflow session not found: ${options.resume}`);
36931
+ fail3(`Workflow session not found: ${workflowId}`);
36932
+ }
36933
+ const maxAttempts = getForwardResumeMaxAttempts(ocrDir);
36934
+ const leaseMs = getForwardResumeLeaseMs(ocrDir);
36935
+ const heartbeatMs = getAgentHeartbeatSeconds(ocrDir) * 1e3;
36936
+ const status = await stateStatus(ocrDir, workflowId, {
36937
+ maxAttempts,
36938
+ heartbeatMs
36939
+ });
36940
+ switch (status.next_action_kind) {
36941
+ case "none":
36942
+ console.error(source_default.green(`Workflow ${workflowId} is already complete \u2014 nothing to resume.`));
36943
+ process.exit(0);
36944
+ break;
36945
+ case "finish":
36946
+ console.error(
36947
+ source_default.yellow(`Workflow ${workflowId}'s round is complete but the session is still open.`)
36948
+ );
36949
+ console.error(source_default.dim("Run `ocr state finish` to close it."));
36950
+ process.exit(0);
36951
+ break;
36952
+ case "abort_or_fresh": {
36953
+ closeForwardResumeExhausted(db, workflowId, maxAttempts);
36954
+ fail3(
36955
+ `Forward-resume attempts exhausted for workflow ${workflowId} (cap ${maxAttempts}). Closed non-success (artifacts preserved). Start a fresh review, or run \`ocr state finish --abort\` if it was already closed.`
36956
+ );
36957
+ break;
36958
+ }
36959
+ case "advance":
36960
+ case "complete_round":
36961
+ case "wait":
36962
+ console.error(
36963
+ source_default.yellow(
36964
+ `Workflow ${workflowId} appears to still be running (phase "${status.current_phase}"). Nothing to resume yet.`
36965
+ )
36966
+ );
36967
+ process.exit(0);
36968
+ break;
36969
+ case "reopen":
36970
+ console.error(
36971
+ source_default.yellow(`Workflow ${workflowId} was closed without a completed round.`)
36972
+ );
36973
+ console.error(source_default.dim("Re-invoke the review skill to finalize it."));
36974
+ process.exit(0);
36975
+ break;
35974
36976
  }
35975
- const latest = getLatestAgentSessionWithVendorId(db, options.resume);
35976
- if (!latest || !latest.vendor_session_id) {
36977
+ const lease = tryAcquireForwardResumeLease(db, workflowId, session.current_round, {
36978
+ leaseMs,
36979
+ maxAttempts
36980
+ });
36981
+ if (!lease.acquired) {
36982
+ if (lease.reason === "cap_exhausted") {
36983
+ closeForwardResumeExhausted(db, workflowId, lease.attemptsUsed);
36984
+ fail3(
36985
+ `Forward-resume attempts exhausted for workflow ${workflowId} (cap ${maxAttempts}). Closed non-success (artifacts preserved). Start a fresh review.`
36986
+ );
36987
+ }
35977
36988
  fail3(
35978
- `No vendor session id has been captured for workflow ${options.resume}. Resume requires at least one journaled agent session with a bound vendor id. Start a fresh review with \`ocr review\` (no --resume).`
36989
+ `A forward-resume is already in progress for workflow ${workflowId} (lease held). Wait for it to finish or retry after the lease expires.`
35979
36990
  );
35980
36991
  }
35981
- const binary = VENDOR_BINARIES[latest.vendor];
35982
- if (!binary) {
35983
- fail3(
35984
- `Unknown vendor "${latest.vendor}" recorded for workflow ${options.resume}. OCR knows how to resume Claude Code and OpenCode; this workflow used something else.`
36992
+ console.error(
36993
+ source_default.dim(
36994
+ `Forward-resuming workflow ${session.id} on branch ${session.branch} from phase "${status.current_phase}" (${status.forward_resume_attempts_remaining ?? "?"} attempt(s) left).`
36995
+ )
36996
+ );
36997
+ const latest = getLatestAgentSessionWithVendorId(db, workflowId);
36998
+ const binary = latest?.vendor ? VENDOR_BINARIES[latest.vendor] : void 0;
36999
+ if (!latest || !latest.vendor_session_id || !binary) {
37000
+ console.error(
37001
+ source_default.yellow(
37002
+ `No resumable vendor session is captured for workflow ${workflowId}.`
37003
+ )
35985
37004
  );
37005
+ console.error(
37006
+ source_default.dim(
37007
+ `Continue it by re-invoking the review skill (\`/ocr-review\`) in your AI CLI \u2014 its Phase 0 reads \`ocr state status --json\` and continues forward from "${status.current_phase}". (${CONTROL_PROMPT})`
37008
+ )
37009
+ );
37010
+ process.exit(0);
35986
37011
  }
35987
37012
  let args;
35988
37013
  try {
@@ -35990,12 +37015,8 @@ var reviewCommand = new Command("review").description("Run or resume an OCR revi
35990
37015
  } catch (err) {
35991
37016
  fail3(err instanceof Error ? err.message : String(err));
35992
37017
  }
35993
- console.error(
35994
- source_default.dim(
35995
- `Resuming workflow ${session.id} on branch ${session.branch} via ${binary}\u2026`
35996
- )
35997
- );
35998
- const child = spawn3(binary, args, {
37018
+ console.error(source_default.dim(`Resuming via ${binary} (continue forward from "${status.current_phase}")\u2026`));
37019
+ const child = spawnBinary(binary, args, {
35999
37020
  stdio: "inherit",
36000
37021
  cwd: targetDir
36001
37022
  });