@open-code-review/cli 1.6.0 → 1.8.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 +20 -4
  2. package/dist/dashboard/client/assets/{_basePickBy-BGuMbEDR.js → _basePickBy-DbLJVCA4.js} +1 -1
  3. package/dist/dashboard/client/assets/{_baseUniq-Bx8loabg.js → _baseUniq-IXEG0cJJ.js} +1 -1
  4. package/dist/dashboard/client/assets/{arc-DUgpt7nY.js → arc-lsKxmOJY.js} +1 -1
  5. package/dist/dashboard/client/assets/{architectureDiagram-VXUJARFQ-D25nt6Xz.js → architectureDiagram-VXUJARFQ-DfMlzFJX.js} +1 -1
  6. package/dist/dashboard/client/assets/{blockDiagram-VD42YOAC-D8PUF3h4.js → blockDiagram-VD42YOAC-bSpnd26J.js} +1 -1
  7. package/dist/dashboard/client/assets/{c4Diagram-YG6GDRKO-lorsCz-I.js → c4Diagram-YG6GDRKO-DPYmVhCZ.js} +1 -1
  8. package/dist/dashboard/client/assets/channel-C--wY_Wd.js +1 -0
  9. package/dist/dashboard/client/assets/{chunk-4BX2VUAB-8lVyfRJM.js → chunk-4BX2VUAB-CI9zC4lV.js} +1 -1
  10. package/dist/dashboard/client/assets/{chunk-55IACEB6-C4SjgsZO.js → chunk-55IACEB6-BqUdJdx5.js} +1 -1
  11. package/dist/dashboard/client/assets/{chunk-B4BG7PRW-BXzTPbH1.js → chunk-B4BG7PRW-DymQrTp-.js} +1 -1
  12. package/dist/dashboard/client/assets/{chunk-DI55MBZ5-Bp7QllDt.js → chunk-DI55MBZ5-lZ_9LKGJ.js} +1 -1
  13. package/dist/dashboard/client/assets/{chunk-FMBD7UC4-B4g9S67N.js → chunk-FMBD7UC4-DC5rgLNm.js} +1 -1
  14. package/dist/dashboard/client/assets/{chunk-QN33PNHL-Dyk7Hc0J.js → chunk-QN33PNHL-BrygpHrX.js} +1 -1
  15. package/dist/dashboard/client/assets/{chunk-QZHKN3VN-DTvkGdnm.js → chunk-QZHKN3VN-CWJqBdNg.js} +1 -1
  16. package/dist/dashboard/client/assets/{chunk-TZMSLE5B-BAeZLvrI.js → chunk-TZMSLE5B-BACgM5pG.js} +1 -1
  17. package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-DoxmMlnf.js +1 -0
  18. package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-DoxmMlnf.js +1 -0
  19. package/dist/dashboard/client/assets/clone-BgvweD4v.js +1 -0
  20. package/dist/dashboard/client/assets/{cose-bilkent-S5V4N54A--6-kzrdu.js → cose-bilkent-S5V4N54A-BYvGIfo0.js} +1 -1
  21. package/dist/dashboard/client/assets/{dagre-6UL2VRFP-D10_QE2P.js → dagre-6UL2VRFP-B1rZyiLJ.js} +1 -1
  22. package/dist/dashboard/client/assets/{diagram-PSM6KHXK-kS1x75Bl.js → diagram-PSM6KHXK-Dvl5dQMd.js} +1 -1
  23. package/dist/dashboard/client/assets/{diagram-QEK2KX5R-D_LLCPas.js → diagram-QEK2KX5R-Cmntmhht.js} +1 -1
  24. package/dist/dashboard/client/assets/{diagram-S2PKOQOG-Duy1t5UO.js → diagram-S2PKOQOG-BqZcpG85.js} +1 -1
  25. package/dist/dashboard/client/assets/{erDiagram-Q2GNP2WA-DyQXwzLf.js → erDiagram-Q2GNP2WA-Cw7BALso.js} +1 -1
  26. package/dist/dashboard/client/assets/{flowDiagram-NV44I4VS-D9U11XVw.js → flowDiagram-NV44I4VS-B_amTHzQ.js} +1 -1
  27. package/dist/dashboard/client/assets/{ganttDiagram-JELNMOA3-STy-TC-3.js → ganttDiagram-JELNMOA3-B1j2-sTo.js} +1 -1
  28. package/dist/dashboard/client/assets/{gitGraphDiagram-V2S2FVAM-B04PgURg.js → gitGraphDiagram-V2S2FVAM-D5BkfAMt.js} +1 -1
  29. package/dist/dashboard/client/assets/{graph-AiGwnT5H.js → graph-B_v15DHv.js} +1 -1
  30. package/dist/dashboard/client/assets/index-UkJZZdYD.js +548 -0
  31. package/dist/dashboard/client/assets/index-Zl---B_3.css +1 -0
  32. package/dist/dashboard/client/assets/{infoDiagram-HS3SLOUP-D4arwl6T.js → infoDiagram-HS3SLOUP-C4dtIkj3.js} +1 -1
  33. package/dist/dashboard/client/assets/{journeyDiagram-XKPGCS4Q-CsKqlKkf.js → journeyDiagram-XKPGCS4Q-hha4Am8v.js} +1 -1
  34. package/dist/dashboard/client/assets/{kanban-definition-3W4ZIXB7-CUFnzQE3.js → kanban-definition-3W4ZIXB7-1EY8l7Ng.js} +1 -1
  35. package/dist/dashboard/client/assets/{layout-BvvYJVPv.js → layout-7SmAbjFT.js} +1 -1
  36. package/dist/dashboard/client/assets/{linear-BiBJkzyE.js → linear-BfjSBezh.js} +1 -1
  37. package/dist/dashboard/client/assets/{mermaid-renderer-DGUmIWXY.js → mermaid-renderer-PPIt-kY4.js} +4 -4
  38. package/dist/dashboard/client/assets/{mindmap-definition-VGOIOE7T-D-Kc9Xgu.js → mindmap-definition-VGOIOE7T-BFpjN9LY.js} +1 -1
  39. package/dist/dashboard/client/assets/{pieDiagram-ADFJNKIX-CooPKLnX.js → pieDiagram-ADFJNKIX-GBbQtDBQ.js} +1 -1
  40. package/dist/dashboard/client/assets/{quadrantDiagram-AYHSOK5B-3soPtaSQ.js → quadrantDiagram-AYHSOK5B-Dm0vOhOw.js} +1 -1
  41. package/dist/dashboard/client/assets/{requirementDiagram-UZGBJVZJ-rE40t0IG.js → requirementDiagram-UZGBJVZJ-BrKONIV8.js} +1 -1
  42. package/dist/dashboard/client/assets/{sankeyDiagram-TZEHDZUN-CrgDF_jW.js → sankeyDiagram-TZEHDZUN-IOobtmDc.js} +1 -1
  43. package/dist/dashboard/client/assets/{sequenceDiagram-WL72ISMW-B628IlDW.js → sequenceDiagram-WL72ISMW-Dnb0bOW5.js} +1 -1
  44. package/dist/dashboard/client/assets/{stateDiagram-FKZM4ZOC-C4yb7S9D.js → stateDiagram-FKZM4ZOC-C9-bf7bn.js} +1 -1
  45. package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-C8Gr4khP.js +1 -0
  46. package/dist/dashboard/client/assets/{timeline-definition-IT6M3QCI-5uLN4f_J.js → timeline-definition-IT6M3QCI-tJogDEHB.js} +1 -1
  47. package/dist/dashboard/client/assets/{treemap-GDKQZRPO-BHXME3bw.js → treemap-GDKQZRPO-DQY6HADq.js} +1 -1
  48. package/dist/dashboard/client/assets/{xychartDiagram-PRI3JC2R-BYTod6eI.js → xychartDiagram-PRI3JC2R-DfxeQmTO.js} +1 -1
  49. package/dist/dashboard/client/index.html +2 -2
  50. package/dist/dashboard/server.js +312 -187
  51. package/dist/index.js +326 -10
  52. package/package.json +2 -2
  53. package/dist/dashboard/client/assets/channel-yW2sWou_.js +0 -1
  54. package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-1pMX5UXO.js +0 -1
  55. package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-1pMX5UXO.js +0 -1
  56. package/dist/dashboard/client/assets/clone-DQwdw3YR.js +0 -1
  57. package/dist/dashboard/client/assets/index-BzQ3i_QR.js +0 -458
  58. package/dist/dashboard/client/assets/index-CGGYXSm-.css +0 -1
  59. package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-BoFeOfLI.js +0 -1
package/dist/index.js CHANGED
@@ -20361,6 +20361,153 @@ function installCommandsForTool(tool, commandsSource, targetDir) {
20361
20361
  return false;
20362
20362
  }
20363
20363
  }
20364
+ var BUILTIN_ICON_MAP = {
20365
+ architect: "blocks",
20366
+ fullstack: "layers",
20367
+ reliability: "activity",
20368
+ "staff-engineer": "compass",
20369
+ principal: "crown",
20370
+ frontend: "layout",
20371
+ backend: "server",
20372
+ infrastructure: "cloud",
20373
+ performance: "gauge",
20374
+ accessibility: "accessibility",
20375
+ data: "database",
20376
+ devops: "rocket",
20377
+ dx: "terminal",
20378
+ mobile: "smartphone",
20379
+ security: "shield-alert",
20380
+ quality: "sparkles",
20381
+ testing: "test-tubes",
20382
+ ai: "bot",
20383
+ "docs-writer": "file-text"
20384
+ };
20385
+ var HOLISTIC_IDS = /* @__PURE__ */ new Set(["architect", "fullstack", "reliability", "staff-engineer", "principal"]);
20386
+ var SPECIALIST_IDS = /* @__PURE__ */ new Set([
20387
+ "frontend",
20388
+ "backend",
20389
+ "infrastructure",
20390
+ "performance",
20391
+ "accessibility",
20392
+ "data",
20393
+ "devops",
20394
+ "dx",
20395
+ "mobile",
20396
+ "security",
20397
+ "quality",
20398
+ "testing",
20399
+ "ai",
20400
+ "docs-writer"
20401
+ ]);
20402
+ var PERSONA_IDS = /* @__PURE__ */ new Set([
20403
+ "martin-fowler",
20404
+ "kent-beck",
20405
+ "john-ousterhout",
20406
+ "anders-hejlsberg",
20407
+ "vladimir-khorikov",
20408
+ "kent-dodds",
20409
+ "tanner-linsley",
20410
+ "kamil-mysliwiec",
20411
+ "sandi-metz",
20412
+ "rich-hickey"
20413
+ ]);
20414
+ function classifyTier(id) {
20415
+ if (PERSONA_IDS.has(id)) return "persona";
20416
+ if (HOLISTIC_IDS.has(id)) return "holistic";
20417
+ if (SPECIALIST_IDS.has(id)) return "specialist";
20418
+ return "custom";
20419
+ }
20420
+ function extractReviewerName(content) {
20421
+ const match = content.match(/^#\s+(.+?)(?:\s+—\s+Reviewer|\s+Reviewer)\s*$/m);
20422
+ if (match?.[1]) return match[1];
20423
+ const titleMatch = content.match(/^#\s+(.+)$/m);
20424
+ return titleMatch?.[1]?.replace(/\s*Reviewer\s*$/, "").trim() ?? "Unknown";
20425
+ }
20426
+ function extractReviewerDescription(content) {
20427
+ const lines = content.split("\n");
20428
+ for (const line of lines) {
20429
+ const trimmed = line.trim();
20430
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith(">")) continue;
20431
+ if (trimmed.startsWith("You are a **") || trimmed.startsWith("You are reviewing")) {
20432
+ return trimmed.replace(/\*\*/g, "").replace(/^You are a /, "").replace(/^You are reviewing code through the lens of .*?\.\s*/, "").trim();
20433
+ }
20434
+ }
20435
+ return "";
20436
+ }
20437
+ function extractFocusAreas(content) {
20438
+ const areas = [];
20439
+ const focusMatch = content.match(/## Your Focus Areas\n([\s\S]*?)(?=\n##|\n---|\z)/);
20440
+ if (focusMatch?.[1]) {
20441
+ const bullets = focusMatch[1].match(/- \*\*(.+?)\*\*/g);
20442
+ if (bullets) {
20443
+ for (const b of bullets) {
20444
+ const m = b.match(/- \*\*(.+?)\*\*/);
20445
+ if (m?.[1]) areas.push(m[1]);
20446
+ }
20447
+ }
20448
+ }
20449
+ return areas;
20450
+ }
20451
+ function extractPersonaFields(content) {
20452
+ const knownMatch = content.match(/>\s*\*\*Known for\*\*:\s*(.+)/);
20453
+ const philMatch = content.match(/>\s*\*\*Philosophy\*\*:\s*([\s\S]*?)(?=\n(?!>)|\n\n)/);
20454
+ const result = {};
20455
+ if (knownMatch?.[1]) result.known_for = knownMatch[1].trim();
20456
+ if (philMatch?.[1]) {
20457
+ result.philosophy = philMatch[1].split("\n").map((l) => l.replace(/^>\s*/, "").trim()).join(" ").trim();
20458
+ }
20459
+ return result;
20460
+ }
20461
+ function generateReviewersMeta(reviewersDir, configPath) {
20462
+ if (!existsSync2(reviewersDir)) return null;
20463
+ const files = readdirSync(reviewersDir).filter((f) => f.endsWith(".md"));
20464
+ if (files.length === 0) return null;
20465
+ const defaultTeamIds = /* @__PURE__ */ new Set();
20466
+ if (existsSync2(configPath)) {
20467
+ try {
20468
+ const configContent = readFileSync3(configPath, "utf-8");
20469
+ const teamMatch = configContent.match(/default_team:\s*\n((?:\s+\w[\w-]*:\s*\d+\s*(?:#[^\n]*)?\n?)*)/);
20470
+ if (teamMatch?.[1]) {
20471
+ const entries = teamMatch[1].matchAll(/\s+([\w-]+):\s*\d+/g);
20472
+ for (const entry of entries) {
20473
+ if (entry[1]) defaultTeamIds.add(entry[1]);
20474
+ }
20475
+ }
20476
+ } catch {
20477
+ }
20478
+ }
20479
+ const reviewers = [];
20480
+ for (const file of files) {
20481
+ const id = file.replace(/\.md$/, "");
20482
+ try {
20483
+ const content = readFileSync3(join2(reviewersDir, file), "utf-8");
20484
+ const tier = classifyTier(id);
20485
+ const isBuiltin = HOLISTIC_IDS.has(id) || SPECIALIST_IDS.has(id) || PERSONA_IDS.has(id);
20486
+ const reviewer = {
20487
+ id,
20488
+ name: extractReviewerName(content),
20489
+ tier,
20490
+ icon: BUILTIN_ICON_MAP[id] ?? (tier === "persona" ? "brain" : "user"),
20491
+ description: extractReviewerDescription(content),
20492
+ focus_areas: extractFocusAreas(content),
20493
+ is_default: defaultTeamIds.has(id),
20494
+ is_builtin: isBuiltin
20495
+ };
20496
+ if (tier === "persona") {
20497
+ const persona = extractPersonaFields(content);
20498
+ if (persona.known_for) reviewer.known_for = persona.known_for;
20499
+ if (persona.philosophy) reviewer.philosophy = persona.philosophy;
20500
+ }
20501
+ reviewers.push(reviewer);
20502
+ } catch {
20503
+ }
20504
+ }
20505
+ return {
20506
+ schema_version: 1,
20507
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
20508
+ reviewers
20509
+ };
20510
+ }
20364
20511
  function installForTool(tool, targetDir) {
20365
20512
  const agentsPath = getAgentsPackagePath();
20366
20513
  const ocrSkillsSource = join2(agentsPath, "skills", "ocr");
@@ -20435,6 +20582,14 @@ function installForTool(tool, targetDir) {
20435
20582
  }
20436
20583
  }
20437
20584
  }
20585
+ const metaPath = join2(ocrDir, "reviewers-meta.json");
20586
+ try {
20587
+ const meta = generateReviewersMeta(reviewersDir, configPath);
20588
+ if (meta) {
20589
+ writeFileSync3(metaPath, JSON.stringify(meta, null, 2) + "\n");
20590
+ }
20591
+ } catch {
20592
+ }
20438
20593
  const commandsOk = installCommandsForTool(tool, commandsSource, targetDir);
20439
20594
  if (!commandsOk) {
20440
20595
  return {
@@ -20600,6 +20755,28 @@ function setConfiguredToolIds(targetDir, toolIds) {
20600
20755
  configuredTools: toolIds
20601
20756
  });
20602
20757
  }
20758
+ function stampCliVersion(targetDir, version) {
20759
+ const existing = loadCliConfig(targetDir) ?? { configuredTools: [] };
20760
+ return saveCliConfig(targetDir, { ...existing, cliVersion: version });
20761
+ }
20762
+ function checkLocalArtifactVersion(targetDir, runningVersion) {
20763
+ const config = loadCliConfig(targetDir);
20764
+ if (!config?.cliVersion) return null;
20765
+ if (config.cliVersion === runningVersion) return null;
20766
+ return { storedVersion: config.cliVersion, runningVersion };
20767
+ }
20768
+ function printLocalVersionHint(drift) {
20769
+ const msg = source_default.yellow(" Local .ocr/ files were installed with ") + source_default.dim(`v${drift.storedVersion}`) + source_default.yellow(" but you're running ") + source_default.green(`v${drift.runningVersion}`) + source_default.yellow(".");
20770
+ const hint = source_default.dim(" Run ") + source_default.bold("ocr update") + source_default.dim(" to refresh local assets.");
20771
+ process.stderr.write(`
20772
+ ${msg}
20773
+ ${hint}
20774
+
20775
+ `);
20776
+ }
20777
+
20778
+ // src/lib/version.ts
20779
+ var CLI_VERSION = true ? "1.8.0" : createRequire(import.meta.url)("../../package.json").version;
20603
20780
 
20604
20781
  // src/lib/deps.ts
20605
20782
  import { execFileSync } from "node:child_process";
@@ -20843,6 +21020,7 @@ var initCommand = new Command("init").description("Set up OCR for AI coding envi
20843
21020
  }
20844
21021
  const successfulToolIds = successful.map((r) => r.tool.id);
20845
21022
  setConfiguredToolIds(targetDir, successfulToolIds);
21023
+ stampCliVersion(targetDir, CLI_VERSION);
20846
21024
  }
20847
21025
  if (failed.length > 0) {
20848
21026
  console.log();
@@ -25453,6 +25631,7 @@ var updateCommand = new Command("update").description("Update OCR assets after p
25453
25631
  if (options.dryRun) {
25454
25632
  console.log(source_default.dim(" Run without --dry-run to apply changes."));
25455
25633
  } else {
25634
+ stampCliVersion(targetDir, CLI_VERSION);
25456
25635
  console.log(source_default.green(" \u2713 Update complete"));
25457
25636
  }
25458
25637
  console.log();
@@ -25616,14 +25795,145 @@ var doctorCommand = new Command("doctor").description("Check OCR installation an
25616
25795
  console.log();
25617
25796
  });
25618
25797
 
25798
+ // src/commands/reviewers.ts
25799
+ import { writeFileSync as writeFileSync8, renameSync as renameSync2 } from "node:fs";
25800
+ import { join as join18 } from "node:path";
25801
+ async function readStdin2() {
25802
+ const chunks = [];
25803
+ for await (const chunk of process.stdin) {
25804
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
25805
+ }
25806
+ const data = Buffer.concat(chunks).toString("utf-8").trim();
25807
+ if (data.length === 0) {
25808
+ throw new Error("No data received on stdin");
25809
+ }
25810
+ return data;
25811
+ }
25812
+ var VALID_TIERS = /* @__PURE__ */ new Set(["holistic", "specialist", "persona", "custom"]);
25813
+ var SLUG_RE = /^[a-z][a-z0-9-]*$/;
25814
+ function validateReviewersMeta(data) {
25815
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
25816
+ throw new Error("Payload must be a JSON object");
25817
+ }
25818
+ const obj = data;
25819
+ if (obj.schema_version !== 1) {
25820
+ throw new Error(`schema_version must be 1, got ${JSON.stringify(obj.schema_version)}`);
25821
+ }
25822
+ if (typeof obj.generated_at !== "string" || obj.generated_at.length === 0) {
25823
+ throw new Error("generated_at must be a non-empty ISO 8601 string");
25824
+ }
25825
+ if (!Array.isArray(obj.reviewers)) {
25826
+ throw new Error("reviewers must be an array");
25827
+ }
25828
+ const seenIds = /* @__PURE__ */ new Set();
25829
+ for (let i = 0; i < obj.reviewers.length; i++) {
25830
+ const r = obj.reviewers[i];
25831
+ const prefix = `reviewers[${i}]`;
25832
+ if (typeof r.id !== "string" || !SLUG_RE.test(r.id)) {
25833
+ throw new Error(`${prefix}.id must be a lowercase slug (got ${JSON.stringify(r.id)})`);
25834
+ }
25835
+ if (seenIds.has(r.id)) {
25836
+ throw new Error(`Duplicate reviewer id: "${r.id}"`);
25837
+ }
25838
+ seenIds.add(r.id);
25839
+ if (typeof r.name !== "string" || r.name.length === 0) {
25840
+ throw new Error(`${prefix}.name must be a non-empty string`);
25841
+ }
25842
+ if (!VALID_TIERS.has(r.tier)) {
25843
+ throw new Error(`${prefix}.tier must be one of: ${[...VALID_TIERS].join(", ")} (got ${JSON.stringify(r.tier)})`);
25844
+ }
25845
+ if (typeof r.description !== "string" || r.description.length === 0) {
25846
+ throw new Error(`${prefix}.description must be a non-empty string`);
25847
+ }
25848
+ if (!Array.isArray(r.focus_areas)) {
25849
+ throw new Error(`${prefix}.focus_areas must be an array`);
25850
+ }
25851
+ if (r.known_for !== void 0 && typeof r.known_for !== "string") {
25852
+ throw new Error(`${prefix}.known_for must be a string if provided`);
25853
+ }
25854
+ if (r.philosophy !== void 0 && typeof r.philosophy !== "string") {
25855
+ throw new Error(`${prefix}.philosophy must be a string if provided`);
25856
+ }
25857
+ }
25858
+ return data;
25859
+ }
25860
+ var syncSubcommand2 = new Command("sync").description("Sync reviewers-meta.json from reviewer markdown files or structured JSON").option("--stdin", "Read reviewers JSON from stdin (for AI-invoked sync)").action(async (options) => {
25861
+ const targetDir = process.cwd();
25862
+ requireOcrSetup(targetDir);
25863
+ const ocrDir = join18(targetDir, ".ocr");
25864
+ if (!options.stdin) {
25865
+ try {
25866
+ const reviewersDir = join18(ocrDir, "skills", "references", "reviewers");
25867
+ const configPath = join18(ocrDir, "config.yaml");
25868
+ const meta = generateReviewersMeta(reviewersDir, configPath);
25869
+ if (!meta || meta.reviewers.length === 0) {
25870
+ console.error(source_default.yellow("No reviewer files found in .ocr/skills/references/reviewers/"));
25871
+ process.exit(1);
25872
+ }
25873
+ const metaPath = join18(ocrDir, "reviewers-meta.json");
25874
+ const tmpPath = metaPath + ".tmp";
25875
+ writeFileSync8(tmpPath, JSON.stringify(meta, null, 2) + "\n");
25876
+ renameSync2(tmpPath, metaPath);
25877
+ const tierCounts = meta.reviewers.reduce(
25878
+ (acc, r) => {
25879
+ acc[r.tier] = (acc[r.tier] ?? 0) + 1;
25880
+ return acc;
25881
+ },
25882
+ {}
25883
+ );
25884
+ const breakdown = Object.entries(tierCounts).map(([tier, count]) => `${count} ${tier}`).join(", ");
25885
+ console.log(source_default.green(`Synced ${meta.reviewers.length} reviewer(s) (${breakdown}).`));
25886
+ } catch (error) {
25887
+ console.error(
25888
+ source_default.red(`Error: ${error instanceof Error ? error.message : "Failed to sync reviewers"}`)
25889
+ );
25890
+ process.exit(1);
25891
+ }
25892
+ return;
25893
+ }
25894
+ try {
25895
+ const raw = await readStdin2();
25896
+ let parsed;
25897
+ try {
25898
+ parsed = JSON.parse(raw);
25899
+ } catch {
25900
+ throw new Error("Invalid JSON on stdin");
25901
+ }
25902
+ const meta = validateReviewersMeta(parsed);
25903
+ const metaPath = join18(ocrDir, "reviewers-meta.json");
25904
+ const tmpPath = metaPath + ".tmp";
25905
+ writeFileSync8(tmpPath, JSON.stringify(meta, null, 2) + "\n");
25906
+ renameSync2(tmpPath, metaPath);
25907
+ const tierCounts = meta.reviewers.reduce(
25908
+ (acc, r) => {
25909
+ acc[r.tier] = (acc[r.tier] ?? 0) + 1;
25910
+ return acc;
25911
+ },
25912
+ {}
25913
+ );
25914
+ const breakdown = Object.entries(tierCounts).map(([tier, count]) => `${count} ${tier}`).join(", ");
25915
+ console.log(
25916
+ source_default.green(`Synced ${meta.reviewers.length} reviewer(s) (${breakdown}).`)
25917
+ );
25918
+ } catch (error) {
25919
+ console.error(
25920
+ source_default.red(
25921
+ `Error: ${error instanceof Error ? error.message : "Failed to sync reviewers"}`
25922
+ )
25923
+ );
25924
+ process.exit(1);
25925
+ }
25926
+ });
25927
+ var reviewersCommand = new Command("reviewers").description("Manage OCR reviewer metadata").addCommand(syncSubcommand2);
25928
+
25619
25929
  // src/lib/update-check.ts
25620
25930
  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";
25931
+ import { join as join19 } from "node:path";
25932
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, mkdirSync as mkdirSync6 } from "node:fs";
25623
25933
  var PACKAGE_NAME = "@open-code-review/cli";
25624
25934
  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");
25935
+ var CACHE_DIR = join19(homedir(), ".ocr");
25936
+ var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
25627
25937
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
25628
25938
  var FETCH_TIMEOUT_MS = 3e3;
25629
25939
  function readCache(cacheFile) {
@@ -25635,8 +25945,8 @@ function readCache(cacheFile) {
25635
25945
  }
25636
25946
  function writeCache(cacheFile, cache) {
25637
25947
  try {
25638
- mkdirSync6(join18(cacheFile, ".."), { recursive: true });
25639
- writeFileSync8(cacheFile, JSON.stringify(cache));
25948
+ mkdirSync6(join19(cacheFile, ".."), { recursive: true });
25949
+ writeFileSync9(cacheFile, JSON.stringify(cache));
25640
25950
  } catch {
25641
25951
  }
25642
25952
  }
@@ -25656,7 +25966,7 @@ async function checkForUpdate(currentVersion, options) {
25656
25966
  if (process.env.CI || process.env.OCR_NO_UPDATE_CHECK) {
25657
25967
  return null;
25658
25968
  }
25659
- const cacheFile = join18(options?.cacheDir ?? CACHE_DIR, "update-check.json");
25969
+ const cacheFile = join19(options?.cacheDir ?? CACHE_DIR, "update-check.json");
25660
25970
  try {
25661
25971
  const cache = readCache(cacheFile);
25662
25972
  if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
@@ -25700,19 +26010,25 @@ ${line2}
25700
26010
  }
25701
26011
 
25702
26012
  // src/index.ts
25703
- var cliVersion = true ? "1.6.0" : createRequire(import.meta.url)("../package.json").version;
25704
26013
  var HUMAN_COMMANDS = /* @__PURE__ */ new Set(["init", "update", "doctor", "dashboard", "progress"]);
25705
26014
  var subcommand = process.argv[2];
25706
- var updateCheck = subcommand && HUMAN_COMMANDS.has(subcommand) ? checkForUpdate(cliVersion) : null;
26015
+ var updateCheck = subcommand && HUMAN_COMMANDS.has(subcommand) ? checkForUpdate(CLI_VERSION) : null;
25707
26016
  var program2 = new Command();
25708
- program2.name("ocr").description("Open Code Review - AI-powered multi-agent code review").version(cliVersion);
26017
+ program2.name("ocr").description("Open Code Review - AI-powered multi-agent code review").version(CLI_VERSION);
25709
26018
  program2.addCommand(initCommand);
25710
26019
  program2.addCommand(progressCommand);
25711
26020
  program2.addCommand(stateCommand);
25712
26021
  program2.addCommand(updateCommand);
25713
26022
  program2.addCommand(dashboardCommand);
25714
26023
  program2.addCommand(doctorCommand);
26024
+ program2.addCommand(reviewersCommand);
25715
26025
  await program2.parseAsync();
26026
+ if (subcommand && HUMAN_COMMANDS.has(subcommand)) {
26027
+ const drift = checkLocalArtifactVersion(process.cwd(), CLI_VERSION);
26028
+ if (drift) {
26029
+ printLocalVersionHint(drift);
26030
+ }
26031
+ }
25716
26032
  if (updateCheck) {
25717
26033
  const updateResult = await Promise.race([
25718
26034
  updateCheck,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-code-review/cli",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "CLI for Open Code Review - Multi-environment setup and progress tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,7 +50,7 @@
50
50
  "ora": "^8.1.1",
51
51
  "socket.io": "^4.8",
52
52
  "sql.js": "^1.14.1",
53
- "@open-code-review/agents": "1.6.0"
53
+ "@open-code-review/agents": "1.8.0"
54
54
  },
55
55
  "publishConfig": {
56
56
  "access": "public"
@@ -1 +0,0 @@
1
- import{ap as o,aq as n}from"./mermaid-renderer-DGUmIWXY.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-BXzTPbH1.js";import{_ as i}from"./mermaid-renderer-DGUmIWXY.js";import"./chunk-FMBD7UC4-B4g9S67N.js";import"./chunk-55IACEB6-C4SjgsZO.js";import"./chunk-QN33PNHL-Dyk7Hc0J.js";import"./index-BzQ3i_QR.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-BXzTPbH1.js";import{_ as i}from"./mermaid-renderer-DGUmIWXY.js";import"./chunk-FMBD7UC4-B4g9S67N.js";import"./chunk-55IACEB6-C4SjgsZO.js";import"./chunk-QN33PNHL-Dyk7Hc0J.js";import"./index-BzQ3i_QR.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
1
- import{b as r}from"./_baseUniq-Bx8loabg.js";var e=4;function a(o){return r(o,e)}export{a as c};