@open-code-review/cli 1.5.0 → 1.6.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 (59) hide show
  1. package/README.md +57 -127
  2. package/dist/dashboard/client/assets/{_basePickBy-BJKCdvle.js → _basePickBy-BGuMbEDR.js} +1 -1
  3. package/dist/dashboard/client/assets/{_baseUniq-L_sxIO0r.js → _baseUniq-Bx8loabg.js} +1 -1
  4. package/dist/dashboard/client/assets/{arc-tqAEcLt5.js → arc-DUgpt7nY.js} +1 -1
  5. package/dist/dashboard/client/assets/{architectureDiagram-VXUJARFQ-CrKQo6Ye.js → architectureDiagram-VXUJARFQ-D25nt6Xz.js} +1 -1
  6. package/dist/dashboard/client/assets/{blockDiagram-VD42YOAC-DXOc89nw.js → blockDiagram-VD42YOAC-D8PUF3h4.js} +1 -1
  7. package/dist/dashboard/client/assets/{c4Diagram-YG6GDRKO-Ba-jYbw0.js → c4Diagram-YG6GDRKO-lorsCz-I.js} +1 -1
  8. package/dist/dashboard/client/assets/channel-yW2sWou_.js +1 -0
  9. package/dist/dashboard/client/assets/{chunk-4BX2VUAB-D1G3HCqL.js → chunk-4BX2VUAB-8lVyfRJM.js} +1 -1
  10. package/dist/dashboard/client/assets/{chunk-55IACEB6-FI7g4AjR.js → chunk-55IACEB6-C4SjgsZO.js} +1 -1
  11. package/dist/dashboard/client/assets/{chunk-B4BG7PRW-DhEGFGWs.js → chunk-B4BG7PRW-BXzTPbH1.js} +1 -1
  12. package/dist/dashboard/client/assets/{chunk-DI55MBZ5-Da3-6ZE4.js → chunk-DI55MBZ5-Bp7QllDt.js} +1 -1
  13. package/dist/dashboard/client/assets/{chunk-FMBD7UC4-D0QLOjiy.js → chunk-FMBD7UC4-B4g9S67N.js} +1 -1
  14. package/dist/dashboard/client/assets/{chunk-QN33PNHL-WkfgpbLo.js → chunk-QN33PNHL-Dyk7Hc0J.js} +1 -1
  15. package/dist/dashboard/client/assets/{chunk-QZHKN3VN-Bqn0IO1w.js → chunk-QZHKN3VN-DTvkGdnm.js} +1 -1
  16. package/dist/dashboard/client/assets/{chunk-TZMSLE5B-CC_K_BeL.js → chunk-TZMSLE5B-BAeZLvrI.js} +1 -1
  17. package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-1pMX5UXO.js +1 -0
  18. package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-1pMX5UXO.js +1 -0
  19. package/dist/dashboard/client/assets/clone-DQwdw3YR.js +1 -0
  20. package/dist/dashboard/client/assets/{cose-bilkent-S5V4N54A-D8urqxIF.js → cose-bilkent-S5V4N54A--6-kzrdu.js} +1 -1
  21. package/dist/dashboard/client/assets/{dagre-6UL2VRFP-w2xS0ztU.js → dagre-6UL2VRFP-D10_QE2P.js} +1 -1
  22. package/dist/dashboard/client/assets/{diagram-PSM6KHXK-DlOtv6zO.js → diagram-PSM6KHXK-kS1x75Bl.js} +1 -1
  23. package/dist/dashboard/client/assets/{diagram-QEK2KX5R-EpxsVLZY.js → diagram-QEK2KX5R-D_LLCPas.js} +1 -1
  24. package/dist/dashboard/client/assets/{diagram-S2PKOQOG-kmITzl42.js → diagram-S2PKOQOG-Duy1t5UO.js} +1 -1
  25. package/dist/dashboard/client/assets/{erDiagram-Q2GNP2WA-Bvyepu_Z.js → erDiagram-Q2GNP2WA-DyQXwzLf.js} +1 -1
  26. package/dist/dashboard/client/assets/{flowDiagram-NV44I4VS-BokLAZN0.js → flowDiagram-NV44I4VS-D9U11XVw.js} +1 -1
  27. package/dist/dashboard/client/assets/{ganttDiagram-JELNMOA3-i5ZSGuTN.js → ganttDiagram-JELNMOA3-STy-TC-3.js} +1 -1
  28. package/dist/dashboard/client/assets/{gitGraphDiagram-V2S2FVAM-CIayQ8P9.js → gitGraphDiagram-V2S2FVAM-B04PgURg.js} +1 -1
  29. package/dist/dashboard/client/assets/{graph-C3ouLF2F.js → graph-AiGwnT5H.js} +1 -1
  30. package/dist/dashboard/client/assets/{index-icxlpW-l.js → index-BzQ3i_QR.js} +109 -107
  31. package/dist/dashboard/client/assets/index-CGGYXSm-.css +1 -0
  32. package/dist/dashboard/client/assets/{infoDiagram-HS3SLOUP-wxe8NO00.js → infoDiagram-HS3SLOUP-D4arwl6T.js} +1 -1
  33. package/dist/dashboard/client/assets/{journeyDiagram-XKPGCS4Q-BeHCbOFN.js → journeyDiagram-XKPGCS4Q-CsKqlKkf.js} +1 -1
  34. package/dist/dashboard/client/assets/{kanban-definition-3W4ZIXB7-DxUlb4wo.js → kanban-definition-3W4ZIXB7-CUFnzQE3.js} +1 -1
  35. package/dist/dashboard/client/assets/{layout-CYsQ5kjv.js → layout-BvvYJVPv.js} +1 -1
  36. package/dist/dashboard/client/assets/{linear-ByuMiLUn.js → linear-BiBJkzyE.js} +1 -1
  37. package/dist/dashboard/client/assets/{mermaid-renderer-cx-n1jFM.js → mermaid-renderer-DGUmIWXY.js} +4 -4
  38. package/dist/dashboard/client/assets/{mindmap-definition-VGOIOE7T-CI5zvW3G.js → mindmap-definition-VGOIOE7T-D-Kc9Xgu.js} +1 -1
  39. package/dist/dashboard/client/assets/{pieDiagram-ADFJNKIX-lC7QV-4L.js → pieDiagram-ADFJNKIX-CooPKLnX.js} +1 -1
  40. package/dist/dashboard/client/assets/{quadrantDiagram-AYHSOK5B-DI7Bn_fF.js → quadrantDiagram-AYHSOK5B-3soPtaSQ.js} +1 -1
  41. package/dist/dashboard/client/assets/{requirementDiagram-UZGBJVZJ-BVuFGUp6.js → requirementDiagram-UZGBJVZJ-rE40t0IG.js} +1 -1
  42. package/dist/dashboard/client/assets/{sankeyDiagram-TZEHDZUN-C-3hBPRk.js → sankeyDiagram-TZEHDZUN-CrgDF_jW.js} +1 -1
  43. package/dist/dashboard/client/assets/{sequenceDiagram-WL72ISMW-CLS6xCbv.js → sequenceDiagram-WL72ISMW-B628IlDW.js} +1 -1
  44. package/dist/dashboard/client/assets/{stateDiagram-FKZM4ZOC-XOLrkoEE.js → stateDiagram-FKZM4ZOC-C4yb7S9D.js} +1 -1
  45. package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-BoFeOfLI.js +1 -0
  46. package/dist/dashboard/client/assets/{timeline-definition-IT6M3QCI-N9m6IkH5.js → timeline-definition-IT6M3QCI-5uLN4f_J.js} +1 -1
  47. package/dist/dashboard/client/assets/{treemap-GDKQZRPO-ayvdfxB1.js → treemap-GDKQZRPO-BHXME3bw.js} +1 -1
  48. package/dist/dashboard/client/assets/{xychartDiagram-PRI3JC2R-CUmVEVIH.js → xychartDiagram-PRI3JC2R-BYTod6eI.js} +1 -1
  49. package/dist/dashboard/client/index.html +2 -2
  50. package/dist/dashboard/server.js +532 -58
  51. package/dist/index.js +763 -220
  52. package/dist/lib/db/index.js +522 -0
  53. package/package.json +2 -2
  54. package/dist/dashboard/client/assets/channel-OmrThJE3.js +0 -1
  55. package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-Dg5ffKNR.js +0 -1
  56. package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-Dg5ffKNR.js +0 -1
  57. package/dist/dashboard/client/assets/clone-CKI4Qu1i.js +0 -1
  58. package/dist/dashboard/client/assets/index-CPEavIIM.css +0 -1
  59. package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-Cy33HZ1p.js +0 -1
package/dist/index.js CHANGED
@@ -2157,7 +2157,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2157
2157
  *
2158
2158
  * @private
2159
2159
  */
2160
- _executeSubCommand(subcommand, args) {
2160
+ _executeSubCommand(subcommand2, args) {
2161
2161
  args = args.slice();
2162
2162
  let launchWithNode = false;
2163
2163
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
@@ -2173,7 +2173,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2173
2173
  }
2174
2174
  this._checkForMissingMandatoryOptions();
2175
2175
  this._checkForConflictingOptions();
2176
- let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
2176
+ let executableFile = subcommand2._executableFile || `${this._name}-${subcommand2._name}`;
2177
2177
  let executableDir = this._executableDir || "";
2178
2178
  if (this._scriptPath) {
2179
2179
  let resolvedScriptPath;
@@ -2189,7 +2189,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2189
2189
  }
2190
2190
  if (executableDir) {
2191
2191
  let localFile = findFile(executableDir, executableFile);
2192
- if (!localFile && !subcommand._executableFile && this._scriptPath) {
2192
+ if (!localFile && !subcommand2._executableFile && this._scriptPath) {
2193
2193
  const legacyName = path2.basename(
2194
2194
  this._scriptPath,
2195
2195
  path2.extname(this._scriptPath)
@@ -2197,7 +2197,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2197
2197
  if (legacyName !== this._name) {
2198
2198
  localFile = findFile(
2199
2199
  executableDir,
2200
- `${legacyName}-${subcommand._name}`
2200
+ `${legacyName}-${subcommand2._name}`
2201
2201
  );
2202
2202
  }
2203
2203
  }
@@ -2217,7 +2217,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2217
2217
  this._checkForMissingExecutable(
2218
2218
  executableFile,
2219
2219
  executableDir,
2220
- subcommand._name
2220
+ subcommand2._name
2221
2221
  );
2222
2222
  args.unshift(executableFile);
2223
2223
  args = incrementNodeInspectorPort(process13.execArgv).concat(args);
@@ -2253,7 +2253,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2253
2253
  this._checkForMissingExecutable(
2254
2254
  executableFile,
2255
2255
  executableDir,
2256
- subcommand._name
2256
+ subcommand2._name
2257
2257
  );
2258
2258
  } else if (err.code === "EACCES") {
2259
2259
  throw new Error(`'${executableFile}' not executable`);
@@ -16283,6 +16283,30 @@ var init_migrations = __esm({
16283
16283
  ALTER TABLE orchestration_events_new RENAME TO orchestration_events;
16284
16284
  CREATE INDEX idx_events_session ON orchestration_events(session_id);
16285
16285
  CREATE INDEX idx_events_type ON orchestration_events(event_type);
16286
+ `
16287
+ },
16288
+ {
16289
+ version: 6,
16290
+ description: "Add orchestrator-first columns to review_rounds for round-meta.json support",
16291
+ sql: `
16292
+ ALTER TABLE review_rounds ADD COLUMN source TEXT DEFAULT NULL;
16293
+ ALTER TABLE review_rounds ADD COLUMN reviewer_count INTEGER DEFAULT 0;
16294
+ ALTER TABLE review_rounds ADD COLUMN total_finding_count INTEGER DEFAULT 0;
16295
+ `
16296
+ },
16297
+ {
16298
+ version: 7,
16299
+ description: "Add category column to review_findings for blocker/should_fix/suggestion classification",
16300
+ sql: `
16301
+ ALTER TABLE review_findings ADD COLUMN category TEXT DEFAULT NULL;
16302
+ `
16303
+ },
16304
+ {
16305
+ version: 8,
16306
+ description: "Add orchestrator-first columns to map_runs for map-meta.json support",
16307
+ sql: `
16308
+ ALTER TABLE map_runs ADD COLUMN source TEXT DEFAULT NULL;
16309
+ ALTER TABLE map_runs ADD COLUMN section_count INTEGER DEFAULT 0;
16286
16310
  `
16287
16311
  }
16288
16312
  ];
@@ -16428,14 +16452,14 @@ __export(db_exports, {
16428
16452
  saveDatabase: () => saveDatabase,
16429
16453
  updateSession: () => updateSession
16430
16454
  });
16431
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, renameSync, writeFileSync as writeFileSync5 } from "node:fs";
16432
- import { dirname as dirname4, join as join10 } from "node:path";
16455
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, renameSync, writeFileSync as writeFileSync6 } from "node:fs";
16456
+ import { dirname as dirname4, join as join11 } from "node:path";
16433
16457
  import { createRequire as createRequire2 } from "node:module";
16434
16458
  import initSqlJs from "sql.js";
16435
16459
  function locateWasm() {
16436
16460
  const require3 = createRequire2(import.meta.url);
16437
16461
  const sqlJsPath = require3.resolve("sql.js");
16438
- return join10(dirname4(sqlJsPath), "sql-wasm.wasm");
16462
+ return join11(dirname4(sqlJsPath), "sql-wasm.wasm");
16439
16463
  }
16440
16464
  function applyPragmas(db) {
16441
16465
  db.run("PRAGMA foreign_keys = ON;");
@@ -16447,7 +16471,7 @@ async function openDatabase(dbPath) {
16447
16471
  if (cached) {
16448
16472
  return cached;
16449
16473
  }
16450
- const wasmBuffer = readFileSync7(locateWasm());
16474
+ const wasmBuffer = readFileSync8(locateWasm());
16451
16475
  const wasmBinary = wasmBuffer.buffer.slice(
16452
16476
  wasmBuffer.byteOffset,
16453
16477
  wasmBuffer.byteOffset + wasmBuffer.byteLength
@@ -16456,8 +16480,8 @@ async function openDatabase(dbPath) {
16456
16480
  wasmBinary
16457
16481
  });
16458
16482
  let db;
16459
- if (existsSync8(dbPath)) {
16460
- const fileBuffer = readFileSync7(dbPath);
16483
+ if (existsSync9(dbPath)) {
16484
+ const fileBuffer = readFileSync8(dbPath);
16461
16485
  db = new SQL.Database(fileBuffer);
16462
16486
  } else {
16463
16487
  db = new SQL.Database();
@@ -16469,23 +16493,23 @@ async function openDatabase(dbPath) {
16469
16493
  function saveDatabase(db, dbPath) {
16470
16494
  const data = db.export();
16471
16495
  const dir = dirname4(dbPath);
16472
- if (!existsSync8(dir)) {
16496
+ if (!existsSync9(dir)) {
16473
16497
  mkdirSync3(dir, { recursive: true });
16474
16498
  }
16475
16499
  const tmpPath = dbPath + ".tmp";
16476
- writeFileSync5(tmpPath, Buffer.from(data));
16500
+ writeFileSync6(tmpPath, Buffer.from(data));
16477
16501
  renameSync(tmpPath, dbPath);
16478
16502
  }
16479
16503
  async function getDb(ocrDir) {
16480
- const dbPath = join10(ocrDir, "data", "ocr.db");
16504
+ const dbPath = join11(ocrDir, "data", "ocr.db");
16481
16505
  return openDatabase(dbPath);
16482
16506
  }
16483
16507
  async function ensureDatabase(ocrDir) {
16484
- const dataDir = join10(ocrDir, "data");
16485
- if (!existsSync8(dataDir)) {
16508
+ const dataDir = join11(ocrDir, "data");
16509
+ if (!existsSync9(dataDir)) {
16486
16510
  mkdirSync3(dataDir, { recursive: true });
16487
16511
  }
16488
- const dbPath = join10(dataDir, "ocr.db");
16512
+ const dbPath = join11(dataDir, "ocr.db");
16489
16513
  const db = await openDatabase(dbPath);
16490
16514
  runMigrations(db);
16491
16515
  saveDatabase(db, dbPath);
@@ -20170,19 +20194,68 @@ function parseToolsArg(toolsArg) {
20170
20194
 
20171
20195
  // src/lib/installer.ts
20172
20196
  import {
20173
- existsSync,
20197
+ existsSync as existsSync2,
20174
20198
  mkdirSync,
20175
20199
  cpSync,
20176
- writeFileSync as writeFileSync2,
20200
+ writeFileSync as writeFileSync3,
20177
20201
  readdirSync,
20178
- readFileSync as readFileSync2,
20202
+ readFileSync as readFileSync3,
20179
20203
  unlinkSync as unlinkSync2
20180
20204
  } from "node:fs";
20181
- import { join, dirname } from "node:path";
20205
+ import { join as join2, dirname } from "node:path";
20182
20206
  import { createRequire } from "node:module";
20207
+
20208
+ // src/lib/gitignore.ts
20209
+ import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
20210
+ import { join } from "node:path";
20211
+ var START_MARKER = "# OCR:START \u2014 managed by open-code-review (do not edit this block)";
20212
+ var END_MARKER = "# OCR:END";
20213
+ var MANAGED_ENTRIES = ["sessions/", "data/", "*.db-shm", "*.db-wal"];
20214
+ var LEGACY_LINES = /* @__PURE__ */ new Set([
20215
+ "# OCR session files",
20216
+ "sessions/",
20217
+ "data",
20218
+ "data/"
20219
+ ]);
20220
+ function buildManagedBlock() {
20221
+ return [START_MARKER, ...MANAGED_ENTRIES, END_MARKER].join("\n");
20222
+ }
20223
+ function escapeRegex(str) {
20224
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20225
+ }
20226
+ function stripLegacyLines(content) {
20227
+ return content.split("\n").filter((line) => !LEGACY_LINES.has(line.trim())).join("\n");
20228
+ }
20229
+ function ensureGitignore(ocrDir) {
20230
+ const gitignorePath = join(ocrDir, ".gitignore");
20231
+ const block = buildManagedBlock();
20232
+ let content = existsSync(gitignorePath) ? readFileSync2(gitignorePath, "utf-8") : "";
20233
+ const blockRegex = new RegExp(
20234
+ `${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`,
20235
+ "g"
20236
+ );
20237
+ if (blockRegex.test(content)) {
20238
+ content = content.replace(
20239
+ new RegExp(
20240
+ `${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`,
20241
+ "g"
20242
+ ),
20243
+ block + "\n"
20244
+ );
20245
+ } else {
20246
+ content = stripLegacyLines(content).trimEnd();
20247
+ if (content.length > 0) {
20248
+ content += "\n\n";
20249
+ }
20250
+ content += block + "\n";
20251
+ }
20252
+ writeFileSync2(gitignorePath, content);
20253
+ }
20254
+
20255
+ // src/lib/installer.ts
20183
20256
  var require2 = createRequire(import.meta.url);
20184
20257
  function ensureDir(dir) {
20185
- if (!existsSync(dir)) {
20258
+ if (!existsSync2(dir)) {
20186
20259
  mkdirSync(dir, { recursive: true });
20187
20260
  }
20188
20261
  }
@@ -20191,8 +20264,8 @@ function getAgentsPackagePath() {
20191
20264
  const agentsPath = require2.resolve("@open-code-review/agents/package.json");
20192
20265
  return dirname(agentsPath);
20193
20266
  } catch {
20194
- const localPath = join(process.cwd(), "packages", "agents");
20195
- if (existsSync(localPath)) {
20267
+ const localPath = join2(process.cwd(), "packages", "agents");
20268
+ if (existsSync2(localPath)) {
20196
20269
  return localPath;
20197
20270
  }
20198
20271
  throw new Error(
@@ -20212,8 +20285,8 @@ function copyDirSafe(src, dest) {
20212
20285
  function copyFileSafe(src, dest) {
20213
20286
  try {
20214
20287
  ensureDir(dirname(dest));
20215
- const content = readFileSync2(src);
20216
- writeFileSync2(dest, content);
20288
+ const content = readFileSync3(src);
20289
+ writeFileSync3(dest, content);
20217
20290
  return true;
20218
20291
  } catch {
20219
20292
  return false;
@@ -20237,8 +20310,8 @@ function extractDescription(content) {
20237
20310
  return match?.[1]?.trim() ?? "OCR command";
20238
20311
  }
20239
20312
  function installCommandsForTool(tool, commandsSource, targetDir) {
20240
- const toolCommandsDir = join(targetDir, tool.commandsDir);
20241
- const centralCommandsDir = join(targetDir, ".ocr", "commands");
20313
+ const toolCommandsDir = join2(targetDir, tool.commandsDir);
20314
+ const centralCommandsDir = join2(targetDir, ".ocr", "commands");
20242
20315
  ensureDir(toolCommandsDir);
20243
20316
  ensureDir(centralCommandsDir);
20244
20317
  try {
@@ -20246,32 +20319,32 @@ function installCommandsForTool(tool, commandsSource, targetDir) {
20246
20319
  (f) => f.endsWith(".md")
20247
20320
  );
20248
20321
  for (const file of commandFiles) {
20249
- const srcPath = join(commandsSource, file);
20322
+ const srcPath = join2(commandsSource, file);
20250
20323
  const normalizedName = file.replace(/^ocr-/, "");
20251
- const centralPath = join(centralCommandsDir, normalizedName);
20324
+ const centralPath = join2(centralCommandsDir, normalizedName);
20252
20325
  if (!copyFileSafe(srcPath, centralPath)) {
20253
20326
  return false;
20254
20327
  }
20255
20328
  }
20256
20329
  if (tool.commandStrategy === "subdirectory") {
20257
- const ocrSubdir = join(toolCommandsDir, "ocr");
20330
+ const ocrSubdir = join2(toolCommandsDir, "ocr");
20258
20331
  ensureDir(ocrSubdir);
20259
20332
  for (const file of commandFiles) {
20260
- const srcPath = join(commandsSource, file);
20261
- const content = readFileSync2(srcPath, "utf-8");
20333
+ const srcPath = join2(commandsSource, file);
20334
+ const content = readFileSync3(srcPath, "utf-8");
20262
20335
  const description = extractDescription(content);
20263
20336
  const normalizedName = file.replace(/^ocr-/, "");
20264
20337
  const refContent = generateCommandReference(
20265
20338
  normalizedName,
20266
20339
  description
20267
20340
  );
20268
- const destPath = join(ocrSubdir, normalizedName);
20269
- writeFileSync2(destPath, refContent);
20341
+ const destPath = join2(ocrSubdir, normalizedName);
20342
+ writeFileSync3(destPath, refContent);
20270
20343
  }
20271
20344
  } else {
20272
20345
  for (const file of commandFiles) {
20273
- const srcPath = join(commandsSource, file);
20274
- const content = readFileSync2(srcPath, "utf-8");
20346
+ const srcPath = join2(commandsSource, file);
20347
+ const content = readFileSync3(srcPath, "utf-8");
20275
20348
  const description = extractDescription(content);
20276
20349
  const normalizedName = file.replace(/^ocr-/, "");
20277
20350
  const destName = `ocr-${normalizedName}`;
@@ -20279,8 +20352,8 @@ function installCommandsForTool(tool, commandsSource, targetDir) {
20279
20352
  normalizedName,
20280
20353
  description
20281
20354
  );
20282
- const destPath = join(toolCommandsDir, destName);
20283
- writeFileSync2(destPath, refContent);
20355
+ const destPath = join2(toolCommandsDir, destName);
20356
+ writeFileSync3(destPath, refContent);
20284
20357
  }
20285
20358
  }
20286
20359
  return true;
@@ -20290,39 +20363,33 @@ function installCommandsForTool(tool, commandsSource, targetDir) {
20290
20363
  }
20291
20364
  function installForTool(tool, targetDir) {
20292
20365
  const agentsPath = getAgentsPackagePath();
20293
- const ocrSkillsSource = join(agentsPath, "skills", "ocr");
20294
- const commandsSource = join(agentsPath, "commands");
20295
- const ocrDir = join(targetDir, ".ocr");
20296
- const ocrSkillsDest = join(ocrDir, "skills");
20366
+ const ocrSkillsSource = join2(agentsPath, "skills", "ocr");
20367
+ const commandsSource = join2(agentsPath, "commands");
20368
+ const ocrDir = join2(targetDir, ".ocr");
20369
+ const ocrSkillsDest = join2(ocrDir, "skills");
20297
20370
  ensureDir(ocrDir);
20298
- ensureDir(join(ocrDir, "sessions"));
20299
- const gitignoreContent = `# OCR session files
20300
- sessions/
20301
- `;
20302
- const gitignorePath = join(ocrDir, ".gitignore");
20303
- if (!existsSync(gitignorePath)) {
20304
- writeFileSync2(gitignorePath, gitignoreContent);
20305
- }
20306
- const configPath = join(ocrDir, "config.yaml");
20371
+ ensureDir(join2(ocrDir, "sessions"));
20372
+ ensureGitignore(ocrDir);
20373
+ const configPath = join2(ocrDir, "config.yaml");
20307
20374
  let existingConfig = null;
20308
- if (existsSync(configPath)) {
20375
+ if (existsSync2(configPath)) {
20309
20376
  try {
20310
- existingConfig = readFileSync2(configPath);
20377
+ existingConfig = readFileSync3(configPath);
20311
20378
  } catch {
20312
20379
  }
20313
20380
  }
20314
- const reviewersDir = join(ocrSkillsDest, "references", "reviewers");
20381
+ const reviewersDir = join2(ocrSkillsDest, "references", "reviewers");
20315
20382
  const existingReviewers = /* @__PURE__ */ new Map();
20316
20383
  const warnings = [];
20317
- if (existsSync(reviewersDir)) {
20384
+ if (existsSync2(reviewersDir)) {
20318
20385
  try {
20319
20386
  const reviewerFiles = readdirSync(reviewersDir).filter(
20320
20387
  (f) => f.endsWith(".md")
20321
20388
  );
20322
20389
  for (const file of reviewerFiles) {
20323
- const filePath = join(reviewersDir, file);
20390
+ const filePath = join2(reviewersDir, file);
20324
20391
  try {
20325
- existingReviewers.set(file, readFileSync2(filePath));
20392
+ existingReviewers.set(file, readFileSync3(filePath));
20326
20393
  } catch (err) {
20327
20394
  const msg = err instanceof Error ? err.message : "unknown error";
20328
20395
  warnings.push(`Could not read reviewer ${file}: ${msg}`);
@@ -20341,17 +20408,17 @@ sessions/
20341
20408
  error: "Failed to install OCR skills to .ocr/"
20342
20409
  };
20343
20410
  }
20344
- const configSource = join(ocrSkillsSource, "assets", "config.yaml");
20411
+ const configSource = join2(ocrSkillsSource, "assets", "config.yaml");
20345
20412
  if (existingConfig) {
20346
20413
  try {
20347
- writeFileSync2(configPath, existingConfig);
20414
+ writeFileSync3(configPath, existingConfig);
20348
20415
  } catch {
20349
20416
  }
20350
- } else if (existsSync(configSource)) {
20417
+ } else if (existsSync2(configSource)) {
20351
20418
  copyFileSafe(configSource, configPath);
20352
20419
  }
20353
- const duplicateConfig = join(ocrSkillsDest, "assets", "config.yaml");
20354
- if (existsSync(duplicateConfig)) {
20420
+ const duplicateConfig = join2(ocrSkillsDest, "assets", "config.yaml");
20421
+ if (existsSync2(duplicateConfig)) {
20355
20422
  try {
20356
20423
  unlinkSync2(duplicateConfig);
20357
20424
  } catch {
@@ -20361,7 +20428,7 @@ sessions/
20361
20428
  ensureDir(reviewersDir);
20362
20429
  for (const [file, content] of existingReviewers) {
20363
20430
  try {
20364
- writeFileSync2(join(reviewersDir, file), content);
20431
+ writeFileSync3(join2(reviewersDir, file), content);
20365
20432
  } catch (err) {
20366
20433
  const msg = err instanceof Error ? err.message : "unknown error";
20367
20434
  warnings.push(`Could not restore reviewer ${file}: ${msg}`);
@@ -20384,27 +20451,27 @@ sessions/
20384
20451
  }
20385
20452
  function detectInstalledTools(targetDir, tools) {
20386
20453
  return tools.filter((tool) => {
20387
- const configPath = join(targetDir, tool.configDir);
20454
+ const configPath = join2(targetDir, tool.configDir);
20388
20455
  if (tool.id === "github-copilot") {
20389
- const copilotInstructions = join(
20456
+ const copilotInstructions = join2(
20390
20457
  targetDir,
20391
20458
  ".github",
20392
20459
  "copilot-instructions.md"
20393
20460
  );
20394
- const copilotDir = join(targetDir, ".github", "copilot");
20395
- const copilotCommands = join(targetDir, ".github", "commands");
20396
- return existsSync(copilotInstructions) || existsSync(copilotDir) || existsSync(copilotCommands);
20461
+ const copilotDir = join2(targetDir, ".github", "copilot");
20462
+ const copilotCommands = join2(targetDir, ".github", "commands");
20463
+ return existsSync2(copilotInstructions) || existsSync2(copilotDir) || existsSync2(copilotCommands);
20397
20464
  }
20398
- return existsSync(configPath);
20465
+ return existsSync2(configPath);
20399
20466
  });
20400
20467
  }
20401
20468
 
20402
20469
  // src/lib/injector.ts
20403
- import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
20404
- import { join as join2 } from "node:path";
20405
- var START_MARKER = "<!-- OCR:START -->";
20406
- var END_MARKER = "<!-- OCR:END -->";
20407
- var OCR_INSTRUCTION_BLOCK = `${START_MARKER}
20470
+ import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
20471
+ import { join as join3 } from "node:path";
20472
+ var START_MARKER2 = "<!-- OCR:START -->";
20473
+ var END_MARKER2 = "<!-- OCR:END -->";
20474
+ var OCR_INSTRUCTION_BLOCK = `${START_MARKER2}
20408
20475
  # Open Code Review Instructions
20409
20476
 
20410
20477
  These instructions are for AI assistants handling code review in this project.
@@ -20423,12 +20490,12 @@ Use \`.ocr/skills/SKILL.md\` to learn:
20423
20490
 
20424
20491
  Keep this managed block so 'ocr init' can refresh the instructions.
20425
20492
 
20426
- ${END_MARKER}`;
20493
+ ${END_MARKER2}`;
20427
20494
  function injectOcrInstructions(filePath) {
20428
20495
  try {
20429
- let content = existsSync2(filePath) ? readFileSync3(filePath, "utf-8") : "";
20496
+ let content = existsSync3(filePath) ? readFileSync4(filePath, "utf-8") : "";
20430
20497
  const regex2 = new RegExp(
20431
- `${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`,
20498
+ `${escapeRegex2(START_MARKER2)}[\\s\\S]*?${escapeRegex2(END_MARKER2)}\\n?`,
20432
20499
  "g"
20433
20500
  );
20434
20501
  content = content.replace(regex2, "");
@@ -20437,18 +20504,18 @@ function injectOcrInstructions(filePath) {
20437
20504
  content += "\n\n";
20438
20505
  }
20439
20506
  content += OCR_INSTRUCTION_BLOCK + "\n";
20440
- writeFileSync3(filePath, content);
20507
+ writeFileSync4(filePath, content);
20441
20508
  return true;
20442
20509
  } catch {
20443
20510
  return false;
20444
20511
  }
20445
20512
  }
20446
- function escapeRegex(str) {
20513
+ function escapeRegex2(str) {
20447
20514
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20448
20515
  }
20449
20516
  function injectIntoProjectFiles(targetDir) {
20450
- const agentsMdPath = join2(targetDir, "AGENTS.md");
20451
- const claudeMdPath = join2(targetDir, "CLAUDE.md");
20517
+ const agentsMdPath = join3(targetDir, "AGENTS.md");
20518
+ const claudeMdPath = join3(targetDir, "CLAUDE.md");
20452
20519
  const agentsMd = injectOcrInstructions(agentsMdPath);
20453
20520
  const claudeMd = injectOcrInstructions(claudeMdPath);
20454
20521
  return { agentsMd, claudeMd };
@@ -20491,19 +20558,19 @@ function printHeader() {
20491
20558
  }
20492
20559
 
20493
20560
  // src/lib/cli-config.ts
20494
- import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
20495
- import { join as join3 } from "node:path";
20561
+ import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
20562
+ import { join as join4 } from "node:path";
20496
20563
  var CLI_CONFIG_FILE = "cli-config.json";
20497
20564
  function getCliConfigPath(targetDir) {
20498
- return join3(targetDir, ".ocr", CLI_CONFIG_FILE);
20565
+ return join4(targetDir, ".ocr", CLI_CONFIG_FILE);
20499
20566
  }
20500
20567
  function loadCliConfig(targetDir) {
20501
20568
  const configPath = getCliConfigPath(targetDir);
20502
- if (!existsSync3(configPath)) {
20569
+ if (!existsSync4(configPath)) {
20503
20570
  return null;
20504
20571
  }
20505
20572
  try {
20506
- const content = readFileSync4(configPath, "utf-8");
20573
+ const content = readFileSync5(configPath, "utf-8");
20507
20574
  return JSON.parse(content);
20508
20575
  } catch {
20509
20576
  return null;
@@ -20516,7 +20583,7 @@ function saveCliConfig(targetDir, config) {
20516
20583
  ...config,
20517
20584
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
20518
20585
  };
20519
- writeFileSync4(configPath, JSON.stringify(configWithMeta, null, 2) + "\n");
20586
+ writeFileSync5(configPath, JSON.stringify(configWithMeta, null, 2) + "\n");
20520
20587
  return true;
20521
20588
  } catch {
20522
20589
  return false;
@@ -22533,8 +22600,8 @@ function watch(paths, options = {}) {
22533
22600
  }
22534
22601
 
22535
22602
  // src/commands/progress.ts
22536
- import { existsSync as existsSync9, readdirSync as readdirSync5, statSync } from "node:fs";
22537
- import { join as join11, basename as basename7 } from "node:path";
22603
+ import { existsSync as existsSync10, readdirSync as readdirSync5, statSync } from "node:fs";
22604
+ import { join as join12, basename as basename7 } from "node:path";
22538
22605
 
22539
22606
  // ../../node_modules/.pnpm/log-update@7.0.2/node_modules/log-update/index.js
22540
22607
  import process12 from "node:process";
@@ -23402,15 +23469,15 @@ var log_update_default = logUpdate;
23402
23469
  var logUpdateStderr = createLogUpdate(process12.stderr);
23403
23470
 
23404
23471
  // src/lib/guards.ts
23405
- import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "node:fs";
23406
- import { join as join6 } from "node:path";
23472
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "node:fs";
23473
+ import { join as join7 } from "node:path";
23407
23474
  function checkOcrSetup(targetDir) {
23408
- const ocrDir = join6(targetDir, ".ocr");
23409
- const skillsDir = join6(ocrDir, "skills");
23410
- const sessionsDir = join6(ocrDir, "sessions");
23411
- const hasOcrDir = existsSync4(ocrDir);
23412
- const hasSkills = existsSync4(skillsDir);
23413
- const hasSessions = existsSync4(sessionsDir);
23475
+ const ocrDir = join7(targetDir, ".ocr");
23476
+ const skillsDir = join7(ocrDir, "skills");
23477
+ const sessionsDir = join7(ocrDir, "sessions");
23478
+ const hasOcrDir = existsSync5(ocrDir);
23479
+ const hasSkills = existsSync5(skillsDir);
23480
+ const hasSessions = existsSync5(sessionsDir);
23414
23481
  return {
23415
23482
  valid: hasOcrDir && hasSkills,
23416
23483
  ocrDir,
@@ -23426,7 +23493,7 @@ function requireOcrSetup(targetDir) {
23426
23493
  console.error();
23427
23494
  console.error(source_default.red.bold(" \u2717 OCR is not set up in this directory"));
23428
23495
  console.error();
23429
- if (!existsSync4(status.ocrDir)) {
23496
+ if (!existsSync5(status.ocrDir)) {
23430
23497
  console.error(source_default.dim(" The .ocr directory was not found."));
23431
23498
  } else if (!status.hasSkills) {
23432
23499
  console.error(source_default.dim(" The .ocr/skills directory is missing."));
@@ -23446,8 +23513,8 @@ function requireOcrSetup(targetDir) {
23446
23513
  return status;
23447
23514
  }
23448
23515
  function ensureSessionsDir(targetDir) {
23449
- const sessionsDir = join6(targetDir, ".ocr", "sessions");
23450
- if (!existsSync4(sessionsDir)) {
23516
+ const sessionsDir = join7(targetDir, ".ocr", "sessions");
23517
+ if (!existsSync5(sessionsDir)) {
23451
23518
  mkdirSync2(sessionsDir, { recursive: true });
23452
23519
  }
23453
23520
  return sessionsDir;
@@ -23463,8 +23530,8 @@ function getStrategy(workflowType) {
23463
23530
  }
23464
23531
 
23465
23532
  // src/lib/progress/detector.ts
23466
- import { existsSync as existsSync5, readdirSync as readdirSync2 } from "node:fs";
23467
- import { join as join7, basename as basename4 } from "node:path";
23533
+ import { existsSync as existsSync6, readdirSync as readdirSync2 } from "node:fs";
23534
+ import { join as join8, basename as basename4 } from "node:path";
23468
23535
 
23469
23536
  // src/lib/progress/session-reader.ts
23470
23537
  init_result_mapper();
@@ -23535,8 +23602,8 @@ function detectWorkflowType(sessionPath, explicitType) {
23535
23602
  } catch {
23536
23603
  }
23537
23604
  }
23538
- const hasMapDir = existsSync5(join7(sessionPath, "map"));
23539
- const hasRoundsDir = existsSync5(join7(sessionPath, "rounds"));
23605
+ const hasMapDir = existsSync6(join8(sessionPath, "map"));
23606
+ const hasRoundsDir = existsSync6(join8(sessionPath, "rounds"));
23540
23607
  if (hasMapDir && !hasRoundsDir) {
23541
23608
  return "map";
23542
23609
  }
@@ -23592,28 +23659,28 @@ function isSessionActive(sessionPath) {
23592
23659
  }
23593
23660
  function detectActiveWorkflows(sessionPath) {
23594
23661
  const activeWorkflows = [];
23595
- const hasRoundsDir = existsSync5(join7(sessionPath, "rounds"));
23662
+ const hasRoundsDir = existsSync6(join8(sessionPath, "rounds"));
23596
23663
  if (hasRoundsDir) {
23597
- const roundsDir = join7(sessionPath, "rounds");
23598
- const rounds = existsSync5(roundsDir) ? readdirSync2(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort() : [];
23664
+ const roundsDir = join8(sessionPath, "rounds");
23665
+ const rounds = existsSync6(roundsDir) ? readdirSync2(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort() : [];
23599
23666
  if (rounds.length > 0) {
23600
23667
  const latestRound = rounds[rounds.length - 1];
23601
- const finalPath = join7(roundsDir, latestRound, "final.md");
23602
- if (!existsSync5(finalPath)) {
23668
+ const finalPath = join8(roundsDir, latestRound, "final.md");
23669
+ if (!existsSync6(finalPath)) {
23603
23670
  activeWorkflows.push("review");
23604
23671
  }
23605
23672
  } else {
23606
23673
  activeWorkflows.push("review");
23607
23674
  }
23608
23675
  }
23609
- const hasMapDir = existsSync5(join7(sessionPath, "map"));
23676
+ const hasMapDir = existsSync6(join8(sessionPath, "map"));
23610
23677
  if (hasMapDir) {
23611
- const runsDir = join7(sessionPath, "map", "runs");
23612
- const runs = existsSync5(runsDir) ? readdirSync2(runsDir).filter((d) => d.match(/^run-\d+$/)).sort() : [];
23678
+ const runsDir = join8(sessionPath, "map", "runs");
23679
+ const runs = existsSync6(runsDir) ? readdirSync2(runsDir).filter((d) => d.match(/^run-\d+$/)).sort() : [];
23613
23680
  if (runs.length > 0) {
23614
23681
  const latestRun = runs[runs.length - 1];
23615
- const mapPath = join7(runsDir, latestRun, "map.md");
23616
- if (!existsSync5(mapPath)) {
23682
+ const mapPath = join8(runsDir, latestRun, "map.md");
23683
+ if (!existsSync6(mapPath)) {
23617
23684
  activeWorkflows.push("map");
23618
23685
  }
23619
23686
  } else {
@@ -23692,8 +23759,8 @@ function padLines(lines) {
23692
23759
  }
23693
23760
 
23694
23761
  // src/lib/progress/review-strategy.ts
23695
- import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
23696
- import { join as join8, basename as basename5 } from "node:path";
23762
+ import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
23763
+ import { join as join9, basename as basename5 } from "node:path";
23697
23764
  var REVIEW_PHASES = [
23698
23765
  { key: "context", label: "Context Discovery" },
23699
23766
  { key: "change-context", label: "Change Context" },
@@ -23705,10 +23772,10 @@ var REVIEW_PHASES = [
23705
23772
  { key: "complete", label: "Complete" }
23706
23773
  ];
23707
23774
  function countFindings(filePath) {
23708
- if (!existsSync6(filePath)) {
23775
+ if (!existsSync7(filePath)) {
23709
23776
  return 0;
23710
23777
  }
23711
- const content = readFileSync5(filePath, "utf-8");
23778
+ const content = readFileSync6(filePath, "utf-8");
23712
23779
  const findingMatches = content.match(/^##\s+(Finding|Issue|Suggestion)/gm);
23713
23780
  return findingMatches?.length ?? 0;
23714
23781
  }
@@ -23722,7 +23789,7 @@ function formatReviewerName(filename) {
23722
23789
  return base.charAt(0).toUpperCase() + base.slice(1);
23723
23790
  }
23724
23791
  function deriveRoundsFromFilesystem(roundsDir) {
23725
- if (!existsSync6(roundsDir)) {
23792
+ if (!existsSync7(roundsDir)) {
23726
23793
  return [];
23727
23794
  }
23728
23795
  const roundDirs = readdirSync3(roundsDir).filter((d) => d.match(/^round-\d+$/)).sort((a, b) => {
@@ -23732,17 +23799,17 @@ function deriveRoundsFromFilesystem(roundsDir) {
23732
23799
  });
23733
23800
  return roundDirs.map((dir) => {
23734
23801
  const roundNum = parseInt(dir.replace("round-", ""));
23735
- const roundPath = join8(roundsDir, dir);
23736
- const reviewsPath = join8(roundPath, "reviews");
23737
- const finalPath = join8(roundPath, "final.md");
23802
+ const roundPath = join9(roundsDir, dir);
23803
+ const reviewsPath = join9(roundPath, "reviews");
23804
+ const finalPath = join9(roundPath, "final.md");
23738
23805
  const reviewers = [];
23739
- if (existsSync6(reviewsPath)) {
23806
+ if (existsSync7(reviewsPath)) {
23740
23807
  const files = readdirSync3(reviewsPath).filter((f) => f.endsWith(".md"));
23741
23808
  reviewers.push(...files.map((f) => f.replace(".md", "")));
23742
23809
  }
23743
23810
  return {
23744
23811
  round: roundNum,
23745
- isComplete: existsSync6(finalPath),
23812
+ isComplete: existsSync7(finalPath),
23746
23813
  reviewers
23747
23814
  };
23748
23815
  });
@@ -23771,19 +23838,19 @@ var ReviewProgressStrategy = class {
23771
23838
  parseFromState(session, state, sessionPath, preservedStartTime) {
23772
23839
  const effectiveStartTime = state.round_started_at ?? state.started_at;
23773
23840
  const startTime = preservedStartTime ?? (effectiveStartTime ? new Date(effectiveStartTime).getTime() : Date.now());
23774
- const roundsDir = join8(sessionPath, "rounds");
23841
+ const roundsDir = join9(sessionPath, "rounds");
23775
23842
  const rounds = deriveRoundsFromFilesystem(roundsDir);
23776
23843
  const highestExistingRound = rounds.length > 0 ? Math.max(...rounds.map((r) => r.round)) : 1;
23777
23844
  const stateRound = state.current_round ?? 1;
23778
23845
  const currentRound = Math.min(stateRound, highestExistingRound);
23779
- const currentRoundDir = join8(roundsDir, `round-${currentRound}`);
23780
- const reviewsDir = join8(currentRoundDir, "reviews");
23846
+ const currentRoundDir = join9(roundsDir, `round-${currentRound}`);
23847
+ const reviewsDir = join9(currentRoundDir, "reviews");
23781
23848
  const reviewers = [];
23782
- if (existsSync6(reviewsDir)) {
23849
+ if (existsSync7(reviewsDir)) {
23783
23850
  const entries = readdirSync3(reviewsDir);
23784
23851
  const reviewFiles = entries.filter((f) => f.endsWith(".md"));
23785
23852
  for (const file of reviewFiles) {
23786
- const reviewPath = join8(reviewsDir, file);
23853
+ const reviewPath = join9(reviewsDir, file);
23787
23854
  const findings = countFindings(reviewPath);
23788
23855
  reviewers.push({
23789
23856
  name: file.replace(".md", ""),
@@ -23793,14 +23860,14 @@ var ReviewProgressStrategy = class {
23793
23860
  });
23794
23861
  }
23795
23862
  }
23796
- const contextComplete = existsSync6(
23797
- join8(sessionPath, "discovered-standards.md")
23863
+ const contextComplete = existsSync7(
23864
+ join9(sessionPath, "discovered-standards.md")
23798
23865
  );
23799
- const changeContextComplete = existsSync6(join8(sessionPath, "context.md"));
23866
+ const changeContextComplete = existsSync7(join9(sessionPath, "context.md"));
23800
23867
  const analysisComplete = changeContextComplete;
23801
23868
  const reviewsComplete = state.phase_number > 4;
23802
- const discourseComplete = existsSync6(join8(currentRoundDir, "discourse.md"));
23803
- const synthesisComplete = existsSync6(join8(currentRoundDir, "final.md"));
23869
+ const discourseComplete = existsSync7(join9(currentRoundDir, "discourse.md"));
23870
+ const synthesisComplete = existsSync7(join9(currentRoundDir, "final.md"));
23804
23871
  return {
23805
23872
  workflowType: "review",
23806
23873
  session,
@@ -23932,8 +23999,8 @@ var ReviewProgressStrategy = class {
23932
23999
  var reviewStrategy = new ReviewProgressStrategy();
23933
24000
 
23934
24001
  // src/lib/progress/map-strategy.ts
23935
- import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "node:fs";
23936
- import { join as join9, basename as basename6 } from "node:path";
24002
+ import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "node:fs";
24003
+ import { join as join10, basename as basename6 } from "node:path";
23937
24004
  var MAP_PHASES = [
23938
24005
  { key: "map-context", label: "Context Discovery" },
23939
24006
  { key: "topology", label: "Topology Analysis" },
@@ -23943,8 +24010,8 @@ var MAP_PHASES = [
23943
24010
  { key: "complete", label: "Complete" }
23944
24011
  ];
23945
24012
  function deriveRunsFromFilesystem(mapDir) {
23946
- const runsDir = join9(mapDir, "runs");
23947
- if (!existsSync7(runsDir)) {
24013
+ const runsDir = join10(mapDir, "runs");
24014
+ if (!existsSync8(runsDir)) {
23948
24015
  return [];
23949
24016
  }
23950
24017
  const runDirs = readdirSync4(runsDir).filter((d) => d.match(/^run-\d+$/)).sort((a, b) => {
@@ -23954,12 +24021,12 @@ function deriveRunsFromFilesystem(mapDir) {
23954
24021
  });
23955
24022
  return runDirs.map((dir) => {
23956
24023
  const runNum = parseInt(dir.replace("run-", ""));
23957
- const runPath = join9(runsDir, dir);
23958
- const mapPath = join9(runPath, "map.md");
24024
+ const runPath = join10(runsDir, dir);
24025
+ const mapPath = join10(runPath, "map.md");
23959
24026
  let fileCount = 0;
23960
- const topologyPath = join9(runPath, "topology.md");
23961
- if (existsSync7(topologyPath)) {
23962
- const content = readFileSync6(topologyPath, "utf-8");
24027
+ const topologyPath = join10(runPath, "topology.md");
24028
+ if (existsSync8(topologyPath)) {
24029
+ const content = readFileSync7(topologyPath, "utf-8");
23963
24030
  const fileListMatch = content.match(
23964
24031
  /## Canonical File List[\s\S]*?```([\s\S]*?)```/
23965
24032
  );
@@ -23969,7 +24036,7 @@ function deriveRunsFromFilesystem(mapDir) {
23969
24036
  }
23970
24037
  return {
23971
24038
  run: runNum,
23972
- isComplete: existsSync7(mapPath),
24039
+ isComplete: existsSync8(mapPath),
23973
24040
  fileCount
23974
24041
  };
23975
24042
  });
@@ -24001,24 +24068,24 @@ var MapProgressStrategy = class {
24001
24068
  parseFromState(session, state, sessionPath, preservedStartTime) {
24002
24069
  const effectiveStartTime = state.map_started_at ?? state.started_at;
24003
24070
  const startTime = preservedStartTime ?? (effectiveStartTime ? new Date(effectiveStartTime).getTime() : Date.now());
24004
- const mapDir = join9(sessionPath, "map");
24071
+ const mapDir = join10(sessionPath, "map");
24005
24072
  const runs = deriveRunsFromFilesystem(mapDir);
24006
24073
  const highestExistingRun = runs.length > 0 ? Math.max(...runs.map((r) => r.run)) : 1;
24007
24074
  const stateRun = state.current_map_run ?? 1;
24008
24075
  const currentRun = Math.min(stateRun, highestExistingRun);
24009
- const currentRunDir = join9(mapDir, "runs", `run-${currentRun}`);
24010
- const contextComplete = existsSync7(
24011
- join9(sessionPath, "discovered-standards.md")
24076
+ const currentRunDir = join10(mapDir, "runs", `run-${currentRun}`);
24077
+ const contextComplete = existsSync8(
24078
+ join10(sessionPath, "discovered-standards.md")
24012
24079
  );
24013
- const topologyComplete = existsSync7(join9(currentRunDir, "topology.md"));
24014
- const flowAnalysisComplete = existsSync7(
24015
- join9(currentRunDir, "flow-analysis.md")
24080
+ const topologyComplete = existsSync8(join10(currentRunDir, "topology.md"));
24081
+ const flowAnalysisComplete = existsSync8(
24082
+ join10(currentRunDir, "flow-analysis.md")
24016
24083
  );
24017
- const requirementsMappingComplete = existsSync7(
24018
- join9(currentRunDir, "requirements-mapping.md")
24084
+ const requirementsMappingComplete = existsSync8(
24085
+ join10(currentRunDir, "requirements-mapping.md")
24019
24086
  );
24020
- const synthesisComplete = existsSync7(join9(currentRunDir, "map.md"));
24021
- const hasRequirements = existsSync7(join9(sessionPath, "requirements.md"));
24087
+ const synthesisComplete = existsSync8(join10(currentRunDir, "map.md"));
24088
+ const hasRequirements = existsSync8(join10(sessionPath, "requirements.md"));
24022
24089
  const flowAnalysts = flowAnalysisComplete ? [
24023
24090
  {
24024
24091
  name: "flow-analyst",
@@ -24170,15 +24237,15 @@ function debounce(fn, delay) {
24170
24237
  };
24171
24238
  }
24172
24239
  function findLatestActiveSession(sessionsDir) {
24173
- if (!existsSync9(sessionsDir)) {
24240
+ if (!existsSync10(sessionsDir)) {
24174
24241
  return null;
24175
24242
  }
24176
24243
  const sessions = readdirSync5(sessionsDir).filter((name) => {
24177
- const sessionPath = join11(sessionsDir, name);
24244
+ const sessionPath = join12(sessionsDir, name);
24178
24245
  return statSync(sessionPath).isDirectory();
24179
24246
  }).sort().reverse();
24180
24247
  for (const session of sessions) {
24181
- const sessionPath = join11(sessionsDir, session);
24248
+ const sessionPath = join12(sessionsDir, session);
24182
24249
  if (isSessionActive(sessionPath)) {
24183
24250
  return session;
24184
24251
  }
@@ -24193,8 +24260,8 @@ function getStrategyForSession(sessionPath, explicitWorkflow) {
24193
24260
  return getStrategy(workflowType) ?? null;
24194
24261
  }
24195
24262
  async function initProgressDb(ocrDir) {
24196
- const dbPath = join11(ocrDir, "data", "ocr.db");
24197
- if (!existsSync9(dbPath)) {
24263
+ const dbPath = join12(ocrDir, "data", "ocr.db");
24264
+ if (!existsSync10(dbPath)) {
24198
24265
  return;
24199
24266
  }
24200
24267
  try {
@@ -24219,11 +24286,11 @@ var progressCommand = new Command("progress").description("Watch real-time progr
24219
24286
  const targetDir = process.cwd();
24220
24287
  requireOcrSetup(targetDir);
24221
24288
  const sessionsDir = ensureSessionsDir(targetDir);
24222
- const ocrDir = join11(targetDir, ".ocr");
24289
+ const ocrDir = join12(targetDir, ".ocr");
24223
24290
  await initProgressDb(ocrDir);
24224
24291
  if (options.session) {
24225
- const sessionPath = join11(sessionsDir, options.session);
24226
- if (!existsSync9(sessionPath)) {
24292
+ const sessionPath = join12(sessionsDir, options.session);
24293
+ if (!existsSync10(sessionPath)) {
24227
24294
  console.error(source_default.red(`Session not found: ${options.session}`));
24228
24295
  process.exit(1);
24229
24296
  }
@@ -24284,7 +24351,7 @@ var progressCommand = new Command("progress").description("Watch real-time progr
24284
24351
  return;
24285
24352
  }
24286
24353
  let currentSession = findLatestActiveSession(sessionsDir);
24287
- let currentSessionPath = currentSession ? join11(sessionsDir, currentSession) : null;
24354
+ let currentSessionPath = currentSession ? join12(sessionsDir, currentSession) : null;
24288
24355
  let sessionWatcher = null;
24289
24356
  const preservedStartTimes = {
24290
24357
  review: void 0,
@@ -24292,11 +24359,11 @@ var progressCommand = new Command("progress").description("Watch real-time progr
24292
24359
  };
24293
24360
  let currentStrategy = null;
24294
24361
  const updateDisplayImpl = () => {
24295
- if (!currentSessionPath || !existsSync9(currentSessionPath) || !isSessionActive(currentSessionPath)) {
24362
+ if (!currentSessionPath || !existsSync10(currentSessionPath) || !isSessionActive(currentSessionPath)) {
24296
24363
  const latestActive = findLatestActiveSession(sessionsDir);
24297
24364
  if (latestActive && latestActive !== currentSession) {
24298
24365
  currentSession = latestActive;
24299
- currentSessionPath = join11(sessionsDir, latestActive);
24366
+ currentSessionPath = join12(sessionsDir, latestActive);
24300
24367
  preservedStartTimes.review = void 0;
24301
24368
  preservedStartTimes.map = void 0;
24302
24369
  currentStrategy = null;
@@ -24309,7 +24376,7 @@ var progressCommand = new Command("progress").description("Watch real-time progr
24309
24376
  currentStrategy = null;
24310
24377
  }
24311
24378
  }
24312
- if (currentSessionPath && existsSync9(currentSessionPath)) {
24379
+ if (currentSessionPath && existsSync10(currentSessionPath)) {
24313
24380
  if (!options.workflow) {
24314
24381
  const activeWorkflows = detectActiveWorkflows(currentSessionPath);
24315
24382
  if (activeWorkflows.length > 1) {
@@ -24363,15 +24430,15 @@ var progressCommand = new Command("progress").description("Watch real-time progr
24363
24430
  watchSession(currentSessionPath);
24364
24431
  }
24365
24432
  const timerInterval = setInterval(updateDisplay, 1e3);
24366
- const watchDir = existsSync9(ocrDir) ? ocrDir : targetDir;
24433
+ const watchDir = existsSync10(ocrDir) ? ocrDir : targetDir;
24367
24434
  const dirWatcher = watch(watchDir, {
24368
24435
  persistent: true,
24369
24436
  ignoreInitial: true,
24370
24437
  depth: 3
24371
24438
  });
24372
24439
  dirWatcher.on("addDir", (dirPath) => {
24373
- const parentDir = join11(dirPath, "..");
24374
- const isDirectChild = parentDir.endsWith("sessions") || parentDir.endsWith(join11(".ocr", "sessions"));
24440
+ const parentDir = join12(dirPath, "..");
24441
+ const isDirectChild = parentDir.endsWith("sessions") || parentDir.endsWith(join12(".ocr", "sessions"));
24375
24442
  if (isDirectChild && !dirPath.endsWith("sessions")) {
24376
24443
  const newSession = basename7(dirPath);
24377
24444
  currentSession = newSession;
@@ -24473,27 +24540,34 @@ function renderCombinedProgress(sessionPath, preservedStartTimes, ocrDir) {
24473
24540
  }
24474
24541
 
24475
24542
  // src/commands/state.ts
24476
- import { existsSync as existsSync11, mkdirSync as mkdirSync4 } from "node:fs";
24477
- import { join as join13 } from "node:path";
24543
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5 } from "node:fs";
24544
+ import { join as join14 } from "node:path";
24478
24545
 
24479
24546
  // src/lib/state/index.ts
24480
24547
  init_db();
24481
- import { existsSync as existsSync10, readdirSync as readdirSync6, statSync as statSync2 } from "node:fs";
24482
- import { join as join12 } from "node:path";
24548
+ import {
24549
+ existsSync as existsSync11,
24550
+ mkdirSync as mkdirSync4,
24551
+ readdirSync as readdirSync6,
24552
+ readFileSync as readFileSync9,
24553
+ statSync as statSync2,
24554
+ writeFileSync as writeFileSync7
24555
+ } from "node:fs";
24556
+ import { join as join13 } from "node:path";
24483
24557
  async function stateInit(params) {
24484
24558
  const { sessionId, branch, workflowType, sessionDir, ocrDir } = params;
24485
24559
  const db = await ensureDatabase(ocrDir);
24486
- const dbPath = join12(ocrDir, "data", "ocr.db");
24560
+ const dbPath = join13(ocrDir, "data", "ocr.db");
24487
24561
  const existing = getSession(db, sessionId);
24488
24562
  if (existing) {
24489
- const roundsDir = join12(sessionDir, "rounds");
24563
+ const roundsDir = join13(sessionDir, "rounds");
24490
24564
  let nextRound = 1;
24491
- if (existsSync10(roundsDir)) {
24565
+ if (existsSync11(roundsDir)) {
24492
24566
  const roundDirs = readdirSync6(roundsDir).filter((d) => /^round-\d+$/.test(d)).map((d) => parseInt(d.replace("round-", ""), 10)).sort((a, b) => a - b);
24493
24567
  if (roundDirs.length > 0) {
24494
24568
  const highest = roundDirs[roundDirs.length - 1];
24495
- const hasFinal = existsSync10(
24496
- join12(roundsDir, `round-${highest}`, "final.md")
24569
+ const hasFinal = existsSync11(
24570
+ join13(roundsDir, `round-${highest}`, "final.md")
24497
24571
  );
24498
24572
  nextRound = hasFinal ? highest + 1 : highest;
24499
24573
  }
@@ -24537,7 +24611,7 @@ async function stateInit(params) {
24537
24611
  async function stateTransition(params) {
24538
24612
  const { sessionId, phase, phaseNumber, round, mapRun, ocrDir } = params;
24539
24613
  const db = await ensureDatabase(ocrDir);
24540
- const dbPath = join12(ocrDir, "data", "ocr.db");
24614
+ const dbPath = join13(ocrDir, "data", "ocr.db");
24541
24615
  const existing = getSession(db, sessionId);
24542
24616
  if (!existing) {
24543
24617
  throw new Error(`Session not found: ${sessionId}`);
@@ -24570,7 +24644,7 @@ async function stateTransition(params) {
24570
24644
  async function stateClose(params) {
24571
24645
  const { sessionId, ocrDir } = params;
24572
24646
  const db = await ensureDatabase(ocrDir);
24573
- const dbPath = join12(ocrDir, "data", "ocr.db");
24647
+ const dbPath = join13(ocrDir, "data", "ocr.db");
24574
24648
  const existing = getSession(db, sessionId);
24575
24649
  if (!existing) {
24576
24650
  throw new Error(`Session not found: ${sessionId}`);
@@ -24635,26 +24709,273 @@ async function resolveActiveSession(ocrDir) {
24635
24709
  sessionDir: session.session_dir
24636
24710
  };
24637
24711
  }
24712
+ function readJsonFromSource(params) {
24713
+ if (params.source === "file") {
24714
+ if (!existsSync11(params.filePath)) {
24715
+ throw new Error(`File not found: ${params.filePath}`);
24716
+ }
24717
+ return readFileSync9(params.filePath, "utf-8");
24718
+ }
24719
+ return params.data;
24720
+ }
24721
+ function parseRawJson(raw, label) {
24722
+ try {
24723
+ return JSON.parse(raw);
24724
+ } catch (err) {
24725
+ throw new Error(
24726
+ `Failed to parse ${label}: ${err instanceof Error ? err.message : "invalid JSON"}`
24727
+ );
24728
+ }
24729
+ }
24730
+ function resolveSessionForCompletion(db, explicitId) {
24731
+ if (explicitId) {
24732
+ const existing = getSession(db, explicitId);
24733
+ if (!existing) throw new Error(`Session not found: ${explicitId}`);
24734
+ return {
24735
+ id: existing.id,
24736
+ session_dir: existing.session_dir,
24737
+ current_round: existing.current_round,
24738
+ current_map_run: existing.current_map_run
24739
+ };
24740
+ }
24741
+ const active = getLatestActiveSession(db);
24742
+ if (!active) throw new Error("No active session found");
24743
+ return {
24744
+ id: active.id,
24745
+ session_dir: active.session_dir,
24746
+ current_round: active.current_round,
24747
+ current_map_run: active.current_map_run
24748
+ };
24749
+ }
24750
+ var VALID_CATEGORIES = /* @__PURE__ */ new Set(["blocker", "should_fix", "suggestion", "style"]);
24751
+ var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info"]);
24752
+ function validateRoundMeta(meta) {
24753
+ if (!meta || typeof meta !== "object") {
24754
+ throw new Error("round-meta.json must be a JSON object");
24755
+ }
24756
+ const obj = meta;
24757
+ if (obj.schema_version !== 1) {
24758
+ throw new Error(
24759
+ `Unsupported schema_version: ${String(obj.schema_version)}. Expected 1.`
24760
+ );
24761
+ }
24762
+ if (typeof obj.verdict !== "string" || obj.verdict.trim().length === 0) {
24763
+ throw new Error("round-meta.json must contain a non-empty verdict string");
24764
+ }
24765
+ if (!Array.isArray(obj.reviewers)) {
24766
+ throw new Error("round-meta.json must contain a reviewers array");
24767
+ }
24768
+ for (const reviewer of obj.reviewers) {
24769
+ if (!reviewer || typeof reviewer !== "object") {
24770
+ throw new Error("Each reviewer must be an object");
24771
+ }
24772
+ const r = reviewer;
24773
+ if (typeof r.type !== "string") {
24774
+ throw new Error("Each reviewer must have a type string");
24775
+ }
24776
+ if (typeof r.instance !== "number") {
24777
+ throw new Error("Each reviewer must have an instance number");
24778
+ }
24779
+ if (!Array.isArray(r.findings)) {
24780
+ throw new Error(`Reviewer ${r.type}-${r.instance} must have a findings array`);
24781
+ }
24782
+ for (const finding of r.findings) {
24783
+ if (!finding || typeof finding !== "object") {
24784
+ throw new Error("Each finding must be an object");
24785
+ }
24786
+ const f = finding;
24787
+ if (typeof f.title !== "string" || f.title.trim().length === 0) {
24788
+ throw new Error("Each finding must have a non-empty title");
24789
+ }
24790
+ if (typeof f.category !== "string" || !VALID_CATEGORIES.has(f.category)) {
24791
+ throw new Error(
24792
+ `Finding "${f.title}" has invalid category: "${String(f.category)}". Must be one of: ${[...VALID_CATEGORIES].join(", ")}`
24793
+ );
24794
+ }
24795
+ if (typeof f.severity !== "string" || !VALID_SEVERITIES.has(f.severity)) {
24796
+ throw new Error(
24797
+ `Finding "${f.title}" has invalid severity: "${String(f.severity)}". Must be one of: ${[...VALID_SEVERITIES].join(", ")}`
24798
+ );
24799
+ }
24800
+ if (typeof f.summary !== "string") {
24801
+ throw new Error(`Finding "${f.title}" must have a summary string`);
24802
+ }
24803
+ if (f.file_path !== void 0 && typeof f.file_path !== "string") {
24804
+ throw new Error(`Finding "${f.title}" has invalid file_path: expected string`);
24805
+ }
24806
+ if (f.line_start !== void 0 && typeof f.line_start !== "number") {
24807
+ throw new Error(`Finding "${f.title}" has invalid line_start: expected number`);
24808
+ }
24809
+ if (f.line_end !== void 0 && typeof f.line_end !== "number") {
24810
+ throw new Error(`Finding "${f.title}" has invalid line_end: expected number`);
24811
+ }
24812
+ if (f.flagged_by !== void 0 && !Array.isArray(f.flagged_by)) {
24813
+ throw new Error(`Finding "${f.title}" has invalid flagged_by: expected array`);
24814
+ }
24815
+ }
24816
+ }
24817
+ return meta;
24818
+ }
24819
+ function computeRoundCounts(meta) {
24820
+ const allFindings = [];
24821
+ for (const reviewer of meta.reviewers) {
24822
+ allFindings.push(...reviewer.findings);
24823
+ }
24824
+ return {
24825
+ blockerCount: allFindings.filter((f) => f.category === "blocker").length,
24826
+ shouldFixCount: allFindings.filter((f) => f.category === "should_fix").length,
24827
+ suggestionCount: allFindings.filter((f) => f.category === "suggestion").length,
24828
+ reviewerCount: meta.reviewers.length,
24829
+ totalFindingCount: allFindings.length
24830
+ };
24831
+ }
24832
+ async function stateRoundComplete(params) {
24833
+ const { ocrDir } = params;
24834
+ const db = await ensureDatabase(ocrDir);
24835
+ const dbPath = join13(ocrDir, "data", "ocr.db");
24836
+ const rawJsonString = readJsonFromSource(params);
24837
+ const label = params.source === "file" ? params.filePath : "stdin";
24838
+ const raw = parseRawJson(rawJsonString, label);
24839
+ const meta = validateRoundMeta(raw);
24840
+ const counts = computeRoundCounts(meta);
24841
+ const session = resolveSessionForCompletion(db, params.sessionId);
24842
+ const roundNumber = params.round ?? session.current_round;
24843
+ let metaPath;
24844
+ if (params.source === "stdin") {
24845
+ const roundDir = join13(session.session_dir, "rounds", `round-${roundNumber}`);
24846
+ mkdirSync4(roundDir, { recursive: true });
24847
+ metaPath = join13(roundDir, "round-meta.json");
24848
+ writeFileSync7(metaPath, JSON.stringify(meta, null, 2));
24849
+ }
24850
+ insertEvent(db, {
24851
+ session_id: session.id,
24852
+ event_type: "round_completed",
24853
+ phase: "synthesis",
24854
+ phase_number: 7,
24855
+ round: roundNumber,
24856
+ metadata: JSON.stringify({
24857
+ verdict: meta.verdict,
24858
+ blocker_count: counts.blockerCount,
24859
+ should_fix_count: counts.shouldFixCount,
24860
+ suggestion_count: counts.suggestionCount,
24861
+ reviewer_count: counts.reviewerCount,
24862
+ total_finding_count: counts.totalFindingCount,
24863
+ source: "orchestrator"
24864
+ })
24865
+ });
24866
+ saveDatabase(db, dbPath);
24867
+ return { sessionId: session.id, round: roundNumber, metaPath };
24868
+ }
24869
+ function validateMapMeta(meta) {
24870
+ if (!meta || typeof meta !== "object") {
24871
+ throw new Error("map-meta.json must be a JSON object");
24872
+ }
24873
+ const obj = meta;
24874
+ if (obj.schema_version !== 1) {
24875
+ throw new Error(
24876
+ `Unsupported schema_version: ${String(obj.schema_version)}. Expected 1.`
24877
+ );
24878
+ }
24879
+ if (!Array.isArray(obj.sections)) {
24880
+ throw new Error("map-meta.json must contain a sections array");
24881
+ }
24882
+ for (const section of obj.sections) {
24883
+ if (!section || typeof section !== "object") {
24884
+ throw new Error("Each section must be an object");
24885
+ }
24886
+ const s = section;
24887
+ if (typeof s.section_number !== "number") {
24888
+ throw new Error("Each section must have a section_number");
24889
+ }
24890
+ if (typeof s.title !== "string" || s.title.trim().length === 0) {
24891
+ throw new Error("Each section must have a non-empty title");
24892
+ }
24893
+ if (!Array.isArray(s.files)) {
24894
+ throw new Error(`Section "${s.title}" must have a files array`);
24895
+ }
24896
+ for (const file of s.files) {
24897
+ if (!file || typeof file !== "object") {
24898
+ throw new Error("Each file must be an object");
24899
+ }
24900
+ const f = file;
24901
+ if (typeof f.file_path !== "string" || f.file_path.trim().length === 0) {
24902
+ throw new Error("Each file must have a non-empty file_path");
24903
+ }
24904
+ if (typeof f.role !== "string") {
24905
+ throw new Error(`File "${f.file_path}" must have a role string`);
24906
+ }
24907
+ if (typeof f.lines_added !== "number") {
24908
+ throw new Error(`File "${f.file_path}" must have a lines_added number`);
24909
+ }
24910
+ if (typeof f.lines_deleted !== "number") {
24911
+ throw new Error(`File "${f.file_path}" must have a lines_deleted number`);
24912
+ }
24913
+ }
24914
+ }
24915
+ if (obj.dependencies !== void 0 && !Array.isArray(obj.dependencies)) {
24916
+ throw new Error("map-meta.json dependencies must be an array if provided");
24917
+ }
24918
+ return meta;
24919
+ }
24920
+ function computeMapCounts(meta) {
24921
+ return {
24922
+ sectionCount: meta.sections.length,
24923
+ fileCount: meta.sections.reduce((sum, s) => sum + s.files.length, 0)
24924
+ };
24925
+ }
24926
+ async function stateMapComplete(params) {
24927
+ const { ocrDir } = params;
24928
+ const db = await ensureDatabase(ocrDir);
24929
+ const dbPath = join13(ocrDir, "data", "ocr.db");
24930
+ const rawJsonString = readJsonFromSource(params);
24931
+ const label = params.source === "file" ? params.filePath : "stdin";
24932
+ const raw = parseRawJson(rawJsonString, label);
24933
+ const meta = validateMapMeta(raw);
24934
+ const counts = computeMapCounts(meta);
24935
+ const session = resolveSessionForCompletion(db, params.sessionId);
24936
+ const mapRunNumber = params.mapRun ?? session.current_map_run;
24937
+ let metaPath;
24938
+ if (params.source === "stdin") {
24939
+ const runDir = join13(session.session_dir, "map", "runs", `run-${mapRunNumber}`);
24940
+ mkdirSync4(runDir, { recursive: true });
24941
+ metaPath = join13(runDir, "map-meta.json");
24942
+ writeFileSync7(metaPath, JSON.stringify(meta, null, 2));
24943
+ }
24944
+ insertEvent(db, {
24945
+ session_id: session.id,
24946
+ event_type: "map_completed",
24947
+ phase: "synthesis",
24948
+ phase_number: 5,
24949
+ round: mapRunNumber,
24950
+ metadata: JSON.stringify({
24951
+ section_count: counts.sectionCount,
24952
+ file_count: counts.fileCount,
24953
+ source: "orchestrator"
24954
+ })
24955
+ });
24956
+ saveDatabase(db, dbPath);
24957
+ return { sessionId: session.id, mapRun: mapRunNumber, metaPath };
24958
+ }
24638
24959
  async function stateSync(ocrDir) {
24639
24960
  const db = await ensureDatabase(ocrDir);
24640
- const dbPath = join12(ocrDir, "data", "ocr.db");
24641
- const sessionsRoot = join12(ocrDir, "sessions");
24642
- if (!existsSync10(sessionsRoot)) {
24961
+ const dbPath = join13(ocrDir, "data", "ocr.db");
24962
+ const sessionsRoot = join13(ocrDir, "sessions");
24963
+ if (!existsSync11(sessionsRoot)) {
24643
24964
  return 0;
24644
24965
  }
24645
24966
  const entries = readdirSync6(sessionsRoot).filter((name) => {
24646
- const fullPath = join12(sessionsRoot, name);
24967
+ const fullPath = join13(sessionsRoot, name);
24647
24968
  return statSync2(fullPath).isDirectory();
24648
24969
  });
24649
24970
  let synced = 0;
24650
24971
  for (const dirName of entries) {
24651
- const dirPath = join12(sessionsRoot, dirName);
24972
+ const dirPath = join13(sessionsRoot, dirName);
24652
24973
  const existing = getSession(db, dirName);
24653
24974
  if (existing) {
24654
24975
  continue;
24655
24976
  }
24656
- const hasRoundsDir = existsSync10(join12(dirPath, "rounds"));
24657
- const hasMapDir = existsSync10(join12(dirPath, "map"));
24977
+ const hasRoundsDir = existsSync11(join13(dirPath, "rounds"));
24978
+ const hasMapDir = existsSync11(join13(dirPath, "map"));
24658
24979
  const workflowType = hasMapDir && !hasRoundsDir ? "map" : "review";
24659
24980
  const branchMatch = dirName.match(/^\d{4}-\d{2}-\d{2}-(.+)$/);
24660
24981
  const branch = branchMatch?.[1] ?? dirName;
@@ -24684,6 +25005,17 @@ async function stateSync(ocrDir) {
24684
25005
  }
24685
25006
 
24686
25007
  // src/commands/state.ts
25008
+ async function readStdin() {
25009
+ const chunks = [];
25010
+ for await (const chunk of process.stdin) {
25011
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
25012
+ }
25013
+ const data = Buffer.concat(chunks).toString("utf-8").trim();
25014
+ if (data.length === 0) {
25015
+ throw new Error("No data received on stdin");
25016
+ }
25017
+ return data;
25018
+ }
24687
25019
  var initSubcommand = new Command("init").description("Initialize a new OCR session").requiredOption("--session-id <id>", "Session ID").requiredOption("--branch <branch>", "Branch name").requiredOption(
24688
25020
  "--workflow-type <type>",
24689
25021
  "Workflow type (review or map)",
@@ -24699,10 +25031,10 @@ var initSubcommand = new Command("init").description("Initialize a new OCR sessi
24699
25031
  async (options) => {
24700
25032
  const targetDir = process.cwd();
24701
25033
  requireOcrSetup(targetDir);
24702
- const ocrDir = join13(targetDir, ".ocr");
24703
- const sessionDir = options.sessionDir ?? join13(ocrDir, "sessions", options.sessionId);
24704
- if (!existsSync11(sessionDir)) {
24705
- mkdirSync4(sessionDir, { recursive: true });
25034
+ const ocrDir = join14(targetDir, ".ocr");
25035
+ const sessionDir = options.sessionDir ?? join14(ocrDir, "sessions", options.sessionId);
25036
+ if (!existsSync12(sessionDir)) {
25037
+ mkdirSync5(sessionDir, { recursive: true });
24706
25038
  }
24707
25039
  try {
24708
25040
  const sessionId = await stateInit({
@@ -24727,7 +25059,24 @@ var transitionSubcommand = new Command("transition").description("Transition ses
24727
25059
  async (options) => {
24728
25060
  const targetDir = process.cwd();
24729
25061
  requireOcrSetup(targetDir);
24730
- const ocrDir = join13(targetDir, ".ocr");
25062
+ const ocrDir = join14(targetDir, ".ocr");
25063
+ const VALID_PHASES = /* @__PURE__ */ new Set([
25064
+ "context",
25065
+ "change-context",
25066
+ "analysis",
25067
+ "reviews",
25068
+ "aggregation",
25069
+ "discourse",
25070
+ "synthesis",
25071
+ "complete",
25072
+ "map-context",
25073
+ "topology",
25074
+ "flow-analysis",
25075
+ "requirements-mapping"
25076
+ ]);
25077
+ if (!VALID_PHASES.has(options.phase)) {
25078
+ throw new Error(`Invalid phase: "${options.phase}". Must be one of: ${[...VALID_PHASES].join(", ")}`);
25079
+ }
24731
25080
  try {
24732
25081
  const sessionId = options.sessionId ?? (await resolveActiveSession(ocrDir)).id;
24733
25082
  await stateTransition({
@@ -24754,7 +25103,7 @@ var transitionSubcommand = new Command("transition").description("Transition ses
24754
25103
  var closeSubcommand = new Command("close").description("Close a session").option("--session-id <id>", "Session ID (auto-detects latest active if omitted)").action(async (options) => {
24755
25104
  const targetDir = process.cwd();
24756
25105
  requireOcrSetup(targetDir);
24757
- const ocrDir = join13(targetDir, ".ocr");
25106
+ const ocrDir = join14(targetDir, ".ocr");
24758
25107
  try {
24759
25108
  const sessionId = options.sessionId ?? (await resolveActiveSession(ocrDir)).id;
24760
25109
  await stateClose({
@@ -24774,7 +25123,7 @@ var closeSubcommand = new Command("close").description("Close a session").option
24774
25123
  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) => {
24775
25124
  const targetDir = process.cwd();
24776
25125
  requireOcrSetup(targetDir);
24777
- const ocrDir = join13(targetDir, ".ocr");
25126
+ const ocrDir = join14(targetDir, ".ocr");
24778
25127
  try {
24779
25128
  const result = await stateShow(ocrDir, options.sessionId);
24780
25129
  if (!result) {
@@ -24843,7 +25192,7 @@ var showSubcommand = new Command("show").description("Show current session state
24843
25192
  var syncSubcommand = new Command("sync").description("Rebuild session state from filesystem artifacts").action(async () => {
24844
25193
  const targetDir = process.cwd();
24845
25194
  requireOcrSetup(targetDir);
24846
- const ocrDir = join13(targetDir, ".ocr");
25195
+ const ocrDir = join14(targetDir, ".ocr");
24847
25196
  try {
24848
25197
  const synced = await stateSync(ocrDir);
24849
25198
  console.log(`Synced ${synced} session${synced !== 1 ? "s" : ""} from filesystem.`);
@@ -24856,19 +25205,117 @@ var syncSubcommand = new Command("sync").description("Rebuild session state from
24856
25205
  process.exit(1);
24857
25206
  }
24858
25207
  });
24859
- var stateCommand = new Command("state").description("Manage OCR session state").addCommand(initSubcommand).addCommand(transitionSubcommand).addCommand(closeSubcommand).addCommand(showSubcommand).addCommand(syncSubcommand);
25208
+ var roundCompleteSubcommand = new Command("round-complete").description("Import structured round data into SQLite").option("--file <path>", "Path to round-meta.json").option("--stdin", "Read round-meta JSON from stdin (recommended)").option("--session-id <id>", "Session ID (auto-detects latest active if omitted)").option("--round <number>", "Round number (auto-detects current if omitted)", parseInt).action(
25209
+ async (options) => {
25210
+ const targetDir = process.cwd();
25211
+ requireOcrSetup(targetDir);
25212
+ const ocrDir = join14(targetDir, ".ocr");
25213
+ if (!options.file && !options.stdin) {
25214
+ console.error(source_default.red("Error: Provide either --file <path> or --stdin"));
25215
+ process.exit(1);
25216
+ }
25217
+ if (options.file && options.stdin) {
25218
+ console.error(source_default.red("Error: --file and --stdin are mutually exclusive"));
25219
+ process.exit(1);
25220
+ }
25221
+ try {
25222
+ let result;
25223
+ if (options.stdin) {
25224
+ const data = await readStdin();
25225
+ result = await stateRoundComplete({
25226
+ source: "stdin",
25227
+ ocrDir,
25228
+ data,
25229
+ sessionId: options.sessionId,
25230
+ round: options.round
25231
+ });
25232
+ } else if (options.file) {
25233
+ result = await stateRoundComplete({
25234
+ source: "file",
25235
+ ocrDir,
25236
+ filePath: options.file,
25237
+ sessionId: options.sessionId,
25238
+ round: options.round
25239
+ });
25240
+ } else {
25241
+ process.exit(1);
25242
+ }
25243
+ console.log(source_default.green("Round data imported successfully."));
25244
+ if (result.metaPath) {
25245
+ console.log(source_default.dim(`Wrote ${result.metaPath}`));
25246
+ }
25247
+ } catch (error) {
25248
+ console.error(
25249
+ source_default.red(
25250
+ `Error: ${error instanceof Error ? error.message : "Failed to import round data"}`
25251
+ )
25252
+ );
25253
+ process.exit(1);
25254
+ }
25255
+ }
25256
+ );
25257
+ var mapCompleteSubcommand = new Command("map-complete").description("Import structured map run data into SQLite").option("--file <path>", "Path to map-meta.json").option("--stdin", "Read map-meta JSON from stdin (recommended)").option("--session-id <id>", "Session ID (auto-detects latest active if omitted)").option("--map-run <number>", "Map run number (auto-detects current if omitted)", parseInt).action(
25258
+ async (options) => {
25259
+ const targetDir = process.cwd();
25260
+ requireOcrSetup(targetDir);
25261
+ const ocrDir = join14(targetDir, ".ocr");
25262
+ if (!options.file && !options.stdin) {
25263
+ console.error(source_default.red("Error: Provide either --file <path> or --stdin"));
25264
+ process.exit(1);
25265
+ }
25266
+ if (options.file && options.stdin) {
25267
+ console.error(source_default.red("Error: --file and --stdin are mutually exclusive"));
25268
+ process.exit(1);
25269
+ }
25270
+ try {
25271
+ let result;
25272
+ if (options.stdin) {
25273
+ const data = await readStdin();
25274
+ result = await stateMapComplete({
25275
+ source: "stdin",
25276
+ ocrDir,
25277
+ data,
25278
+ sessionId: options.sessionId,
25279
+ mapRun: options.mapRun
25280
+ });
25281
+ } else if (options.file) {
25282
+ result = await stateMapComplete({
25283
+ source: "file",
25284
+ ocrDir,
25285
+ filePath: options.file,
25286
+ sessionId: options.sessionId,
25287
+ mapRun: options.mapRun
25288
+ });
25289
+ } else {
25290
+ process.exit(1);
25291
+ }
25292
+ console.log(source_default.green("Map data imported successfully."));
25293
+ if (result.metaPath) {
25294
+ console.log(source_default.dim(`Wrote ${result.metaPath}`));
25295
+ }
25296
+ } catch (error) {
25297
+ console.error(
25298
+ source_default.red(
25299
+ `Error: ${error instanceof Error ? error.message : "Failed to import map data"}`
25300
+ )
25301
+ );
25302
+ process.exit(1);
25303
+ }
25304
+ }
25305
+ );
25306
+ var stateCommand = new Command("state").description("Manage OCR session state").addCommand(initSubcommand).addCommand(transitionSubcommand).addCommand(closeSubcommand).addCommand(showSubcommand).addCommand(syncSubcommand).addCommand(roundCompleteSubcommand).addCommand(mapCompleteSubcommand);
24860
25307
 
24861
25308
  // src/commands/update.ts
24862
- import { existsSync as existsSync12 } from "node:fs";
24863
- import { join as join14 } from "node:path";
25309
+ import { existsSync as existsSync13 } from "node:fs";
25310
+ import { join as join15 } from "node:path";
24864
25311
  function detectConfiguredTools(targetDir) {
24865
25312
  return AI_TOOLS.filter((tool) => {
24866
25313
  if (tool.commandStrategy === "subdirectory") {
24867
- const ocrDir = join14(targetDir, tool.commandsDir, "ocr");
24868
- return existsSync12(ocrDir);
25314
+ const ocrDir = join15(targetDir, tool.commandsDir, "ocr");
25315
+ return existsSync13(ocrDir);
24869
25316
  } else {
24870
- const reviewCmd = join14(targetDir, tool.commandsDir, "ocr-review.md");
24871
- return existsSync12(reviewCmd);
25317
+ const reviewCmd = join15(targetDir, tool.commandsDir, "ocr-review.md");
25318
+ return existsSync13(reviewCmd);
24872
25319
  }
24873
25320
  });
24874
25321
  }
@@ -24942,6 +25389,7 @@ var updateCommand = new Command("update").description("Update OCR assets after p
24942
25389
  const result = installForTool(tool, targetDir);
24943
25390
  results.push(result);
24944
25391
  }
25392
+ ensureGitignore(join15(targetDir, ".ocr"));
24945
25393
  spinner.stop();
24946
25394
  const successful = results.filter((r) => r.success);
24947
25395
  const failed = results.filter((r) => !r.success);
@@ -24977,10 +25425,10 @@ var updateCommand = new Command("update").description("Update OCR assets after p
24977
25425
  if (updateInject) {
24978
25426
  if (options.dryRun) {
24979
25427
  console.log(source_default.dim(" Would update:"));
24980
- if (existsSync12(join14(targetDir, "AGENTS.md"))) {
25428
+ if (existsSync13(join15(targetDir, "AGENTS.md"))) {
24981
25429
  console.log(source_default.dim(" \u2022 AGENTS.md (OCR managed block)"));
24982
25430
  }
24983
- if (existsSync12(join14(targetDir, "CLAUDE.md"))) {
25431
+ if (existsSync13(join15(targetDir, "CLAUDE.md"))) {
24984
25432
  console.log(source_default.dim(" \u2022 CLAUDE.md (OCR managed block)"));
24985
25433
  }
24986
25434
  console.log();
@@ -25011,14 +25459,14 @@ var updateCommand = new Command("update").description("Update OCR assets after p
25011
25459
  });
25012
25460
 
25013
25461
  // src/commands/dashboard.ts
25014
- import { existsSync as existsSync13 } from "node:fs";
25015
- import { join as join15, dirname as dirname5 } from "node:path";
25462
+ import { existsSync as existsSync14 } from "node:fs";
25463
+ import { join as join16, dirname as dirname5 } from "node:path";
25016
25464
  import { fileURLToPath } from "node:url";
25017
25465
  init_db();
25018
25466
  var __filename = fileURLToPath(import.meta.url);
25019
25467
  var __dirname = dirname5(__filename);
25020
25468
  function resolveServerPath() {
25021
- return join15(__dirname, "dashboard", "server.js");
25469
+ return join16(__dirname, "dashboard", "server.js");
25022
25470
  }
25023
25471
  var dashboardCommand = new Command("dashboard").description("Start the OCR dashboard web interface").option("-p, --port <port>", "Port to run the server on", "4173").option("--no-open", "Don't open the browser automatically").action(
25024
25472
  async (options) => {
@@ -25029,7 +25477,7 @@ var dashboardCommand = new Command("dashboard").description("Start the OCR dashb
25029
25477
  console.error(source_default.red(`Error: Invalid port "${options.port}". Must be 1-65535.`));
25030
25478
  process.exit(1);
25031
25479
  }
25032
- const ocrDir = join15(targetDir, ".ocr");
25480
+ const ocrDir = join16(targetDir, ".ocr");
25033
25481
  try {
25034
25482
  await ensureDatabase(ocrDir);
25035
25483
  closeAllDatabases();
@@ -25043,7 +25491,7 @@ var dashboardCommand = new Command("dashboard").description("Start the OCR dashb
25043
25491
  process.exit(1);
25044
25492
  }
25045
25493
  const serverPath = resolveServerPath();
25046
- if (!existsSync13(serverPath)) {
25494
+ if (!existsSync14(serverPath)) {
25047
25495
  console.error(source_default.red("Error: Dashboard server bundle not found."));
25048
25496
  console.error(
25049
25497
  source_default.dim(` Expected at: ${serverPath}`)
@@ -25077,8 +25525,8 @@ var dashboardCommand = new Command("dashboard").description("Start the OCR dashb
25077
25525
  );
25078
25526
 
25079
25527
  // src/commands/doctor.ts
25080
- import { existsSync as existsSync14 } from "node:fs";
25081
- import { join as join16 } from "node:path";
25528
+ import { existsSync as existsSync15 } from "node:fs";
25529
+ import { join as join17 } from "node:path";
25082
25530
  var doctorCommand = new Command("doctor").description("Check OCR installation and verify all dependencies").action(() => {
25083
25531
  printHeader();
25084
25532
  const targetDir = process.cwd();
@@ -25092,10 +25540,10 @@ var doctorCommand = new Command("doctor").description("Check OCR installation an
25092
25540
  console.log(source_default.bold(" OCR Installation"));
25093
25541
  console.log();
25094
25542
  const ocrStatus = checkOcrSetup(targetDir);
25095
- const configPath = join16(targetDir, ".ocr", "config.yaml");
25096
- const dbPath = join16(targetDir, ".ocr", "data", "ocr.db");
25097
- const hasConfig = existsSync14(configPath);
25098
- const hasDb = existsSync14(dbPath);
25543
+ const configPath = join17(targetDir, ".ocr", "config.yaml");
25544
+ const dbPath = join17(targetDir, ".ocr", "data", "ocr.db");
25545
+ const hasConfig = existsSync15(configPath);
25546
+ const hasDb = existsSync15(dbPath);
25099
25547
  const ocrChecks = [
25100
25548
  { label: ".ocr/skills/", ok: ocrStatus.hasSkills },
25101
25549
  { label: ".ocr/sessions/", ok: ocrStatus.hasSessions },
@@ -25168,8 +25616,94 @@ var doctorCommand = new Command("doctor").description("Check OCR installation an
25168
25616
  console.log();
25169
25617
  });
25170
25618
 
25619
+ // src/lib/update-check.ts
25620
+ import { homedir } from "node:os";
25621
+ import { join as join18 } from "node:path";
25622
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "node:fs";
25623
+ var PACKAGE_NAME = "@open-code-review/cli";
25624
+ var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
25625
+ var CACHE_DIR = join18(homedir(), ".ocr");
25626
+ var CACHE_FILE = join18(CACHE_DIR, "update-check.json");
25627
+ var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
25628
+ var FETCH_TIMEOUT_MS = 3e3;
25629
+ function readCache(cacheFile) {
25630
+ try {
25631
+ return JSON.parse(readFileSync10(cacheFile, "utf-8"));
25632
+ } catch {
25633
+ return null;
25634
+ }
25635
+ }
25636
+ function writeCache(cacheFile, cache) {
25637
+ try {
25638
+ mkdirSync6(join18(cacheFile, ".."), { recursive: true });
25639
+ writeFileSync8(cacheFile, JSON.stringify(cache));
25640
+ } catch {
25641
+ }
25642
+ }
25643
+ function isNewer(latest, current) {
25644
+ const l = latest.split(".").map(Number);
25645
+ const c = current.split(".").map(Number);
25646
+ for (let i = 0; i < 3; i++) {
25647
+ if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
25648
+ if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
25649
+ }
25650
+ return false;
25651
+ }
25652
+ function detectUpdateCommand() {
25653
+ return `npm i -g ${PACKAGE_NAME}@latest && ocr update`;
25654
+ }
25655
+ async function checkForUpdate(currentVersion, options) {
25656
+ if (process.env.CI || process.env.OCR_NO_UPDATE_CHECK) {
25657
+ return null;
25658
+ }
25659
+ const cacheFile = join18(options?.cacheDir ?? CACHE_DIR, "update-check.json");
25660
+ try {
25661
+ const cache = readCache(cacheFile);
25662
+ if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
25663
+ if (!cache.latestVersion) return null;
25664
+ if (!isNewer(cache.latestVersion, currentVersion)) return null;
25665
+ return {
25666
+ updateAvailable: true,
25667
+ currentVersion,
25668
+ latestVersion: cache.latestVersion,
25669
+ updateCommand: detectUpdateCommand()
25670
+ };
25671
+ }
25672
+ const response = await fetch(REGISTRY_URL, {
25673
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
25674
+ });
25675
+ const data = await response.json();
25676
+ const latestVersion = data.version ?? null;
25677
+ writeCache(cacheFile, { lastCheck: Date.now(), latestVersion });
25678
+ if (!latestVersion || !isNewer(latestVersion, currentVersion)) {
25679
+ return null;
25680
+ }
25681
+ return {
25682
+ updateAvailable: true,
25683
+ currentVersion,
25684
+ latestVersion,
25685
+ updateCommand: detectUpdateCommand()
25686
+ };
25687
+ } catch {
25688
+ writeCache(cacheFile, { lastCheck: Date.now(), latestVersion: null });
25689
+ return null;
25690
+ }
25691
+ }
25692
+ function printUpdateNotification(result) {
25693
+ const line1 = source_default.yellow(" Update available: ") + source_default.dim(result.currentVersion) + source_default.yellow(" \u2192 ") + source_default.green(result.latestVersion);
25694
+ const line2 = source_default.dim(" Run: ") + source_default.bold(result.updateCommand);
25695
+ process.stderr.write(`
25696
+ ${line1}
25697
+ ${line2}
25698
+
25699
+ `);
25700
+ }
25701
+
25171
25702
  // src/index.ts
25172
- var cliVersion = true ? "1.5.0" : createRequire(import.meta.url)("../package.json").version;
25703
+ var cliVersion = true ? "1.6.0" : createRequire(import.meta.url)("../package.json").version;
25704
+ var HUMAN_COMMANDS = /* @__PURE__ */ new Set(["init", "update", "doctor", "dashboard", "progress"]);
25705
+ var subcommand = process.argv[2];
25706
+ var updateCheck = subcommand && HUMAN_COMMANDS.has(subcommand) ? checkForUpdate(cliVersion) : null;
25173
25707
  var program2 = new Command();
25174
25708
  program2.name("ocr").description("Open Code Review - AI-powered multi-agent code review").version(cliVersion);
25175
25709
  program2.addCommand(initCommand);
@@ -25178,7 +25712,16 @@ program2.addCommand(stateCommand);
25178
25712
  program2.addCommand(updateCommand);
25179
25713
  program2.addCommand(dashboardCommand);
25180
25714
  program2.addCommand(doctorCommand);
25181
- program2.parse();
25715
+ await program2.parseAsync();
25716
+ if (updateCheck) {
25717
+ const updateResult = await Promise.race([
25718
+ updateCheck,
25719
+ new Promise((r) => setTimeout(() => r(null), 500))
25720
+ ]);
25721
+ if (updateResult?.updateAvailable) {
25722
+ printUpdateNotification(updateResult);
25723
+ }
25724
+ }
25182
25725
  /*! Bundled license information:
25183
25726
 
25184
25727
  chokidar/esm/index.js: