@open-code-review/cli 1.5.1 → 1.7.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.
- package/README.md +67 -121
- package/dist/dashboard/client/assets/{_basePickBy-BJKCdvle.js → _basePickBy-DbLJVCA4.js} +1 -1
- package/dist/dashboard/client/assets/{_baseUniq-L_sxIO0r.js → _baseUniq-IXEG0cJJ.js} +1 -1
- package/dist/dashboard/client/assets/{arc-tqAEcLt5.js → arc-lsKxmOJY.js} +1 -1
- package/dist/dashboard/client/assets/{architectureDiagram-VXUJARFQ-CrKQo6Ye.js → architectureDiagram-VXUJARFQ-DfMlzFJX.js} +1 -1
- package/dist/dashboard/client/assets/{blockDiagram-VD42YOAC-DXOc89nw.js → blockDiagram-VD42YOAC-bSpnd26J.js} +1 -1
- package/dist/dashboard/client/assets/{c4Diagram-YG6GDRKO-Ba-jYbw0.js → c4Diagram-YG6GDRKO-DPYmVhCZ.js} +1 -1
- package/dist/dashboard/client/assets/channel-C--wY_Wd.js +1 -0
- package/dist/dashboard/client/assets/{chunk-4BX2VUAB-D1G3HCqL.js → chunk-4BX2VUAB-CI9zC4lV.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-55IACEB6-FI7g4AjR.js → chunk-55IACEB6-BqUdJdx5.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-B4BG7PRW-DhEGFGWs.js → chunk-B4BG7PRW-DymQrTp-.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-DI55MBZ5-Da3-6ZE4.js → chunk-DI55MBZ5-lZ_9LKGJ.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-FMBD7UC4-D0QLOjiy.js → chunk-FMBD7UC4-DC5rgLNm.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-QN33PNHL-WkfgpbLo.js → chunk-QN33PNHL-BrygpHrX.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-QZHKN3VN-Bqn0IO1w.js → chunk-QZHKN3VN-CWJqBdNg.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-TZMSLE5B-CC_K_BeL.js → chunk-TZMSLE5B-BACgM5pG.js} +1 -1
- package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-DoxmMlnf.js +1 -0
- package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-DoxmMlnf.js +1 -0
- package/dist/dashboard/client/assets/clone-BgvweD4v.js +1 -0
- package/dist/dashboard/client/assets/{cose-bilkent-S5V4N54A-D8urqxIF.js → cose-bilkent-S5V4N54A-BYvGIfo0.js} +1 -1
- package/dist/dashboard/client/assets/{dagre-6UL2VRFP-w2xS0ztU.js → dagre-6UL2VRFP-B1rZyiLJ.js} +1 -1
- package/dist/dashboard/client/assets/{diagram-PSM6KHXK-DlOtv6zO.js → diagram-PSM6KHXK-Dvl5dQMd.js} +1 -1
- package/dist/dashboard/client/assets/{diagram-QEK2KX5R-EpxsVLZY.js → diagram-QEK2KX5R-Cmntmhht.js} +1 -1
- package/dist/dashboard/client/assets/{diagram-S2PKOQOG-kmITzl42.js → diagram-S2PKOQOG-BqZcpG85.js} +1 -1
- package/dist/dashboard/client/assets/{erDiagram-Q2GNP2WA-Bvyepu_Z.js → erDiagram-Q2GNP2WA-Cw7BALso.js} +1 -1
- package/dist/dashboard/client/assets/{flowDiagram-NV44I4VS-BokLAZN0.js → flowDiagram-NV44I4VS-B_amTHzQ.js} +1 -1
- package/dist/dashboard/client/assets/{ganttDiagram-JELNMOA3-i5ZSGuTN.js → ganttDiagram-JELNMOA3-B1j2-sTo.js} +1 -1
- package/dist/dashboard/client/assets/{gitGraphDiagram-V2S2FVAM-CIayQ8P9.js → gitGraphDiagram-V2S2FVAM-D5BkfAMt.js} +1 -1
- package/dist/dashboard/client/assets/{graph-C3ouLF2F.js → graph-B_v15DHv.js} +1 -1
- package/dist/dashboard/client/assets/index-UkJZZdYD.js +548 -0
- package/dist/dashboard/client/assets/index-Zl---B_3.css +1 -0
- package/dist/dashboard/client/assets/{infoDiagram-HS3SLOUP-wxe8NO00.js → infoDiagram-HS3SLOUP-C4dtIkj3.js} +1 -1
- package/dist/dashboard/client/assets/{journeyDiagram-XKPGCS4Q-BeHCbOFN.js → journeyDiagram-XKPGCS4Q-hha4Am8v.js} +1 -1
- package/dist/dashboard/client/assets/{kanban-definition-3W4ZIXB7-DxUlb4wo.js → kanban-definition-3W4ZIXB7-1EY8l7Ng.js} +1 -1
- package/dist/dashboard/client/assets/{layout-CYsQ5kjv.js → layout-7SmAbjFT.js} +1 -1
- package/dist/dashboard/client/assets/{linear-ByuMiLUn.js → linear-BfjSBezh.js} +1 -1
- package/dist/dashboard/client/assets/{mermaid-renderer-cx-n1jFM.js → mermaid-renderer-PPIt-kY4.js} +4 -4
- package/dist/dashboard/client/assets/{mindmap-definition-VGOIOE7T-CI5zvW3G.js → mindmap-definition-VGOIOE7T-BFpjN9LY.js} +1 -1
- package/dist/dashboard/client/assets/{pieDiagram-ADFJNKIX-lC7QV-4L.js → pieDiagram-ADFJNKIX-GBbQtDBQ.js} +1 -1
- package/dist/dashboard/client/assets/{quadrantDiagram-AYHSOK5B-DI7Bn_fF.js → quadrantDiagram-AYHSOK5B-Dm0vOhOw.js} +1 -1
- package/dist/dashboard/client/assets/{requirementDiagram-UZGBJVZJ-BVuFGUp6.js → requirementDiagram-UZGBJVZJ-BrKONIV8.js} +1 -1
- package/dist/dashboard/client/assets/{sankeyDiagram-TZEHDZUN-C-3hBPRk.js → sankeyDiagram-TZEHDZUN-IOobtmDc.js} +1 -1
- package/dist/dashboard/client/assets/{sequenceDiagram-WL72ISMW-CLS6xCbv.js → sequenceDiagram-WL72ISMW-Dnb0bOW5.js} +1 -1
- package/dist/dashboard/client/assets/{stateDiagram-FKZM4ZOC-XOLrkoEE.js → stateDiagram-FKZM4ZOC-C9-bf7bn.js} +1 -1
- package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-C8Gr4khP.js +1 -0
- package/dist/dashboard/client/assets/{timeline-definition-IT6M3QCI-N9m6IkH5.js → timeline-definition-IT6M3QCI-tJogDEHB.js} +1 -1
- package/dist/dashboard/client/assets/{treemap-GDKQZRPO-ayvdfxB1.js → treemap-GDKQZRPO-DQY6HADq.js} +1 -1
- package/dist/dashboard/client/assets/{xychartDiagram-PRI3JC2R-CUmVEVIH.js → xychartDiagram-PRI3JC2R-DfxeQmTO.js} +1 -1
- package/dist/dashboard/client/index.html +2 -2
- package/dist/dashboard/server.js +810 -206
- package/dist/index.js +810 -15
- package/dist/lib/db/index.js +522 -0
- package/package.json +2 -2
- package/dist/dashboard/client/assets/channel-OmrThJE3.js +0 -1
- package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-Dg5ffKNR.js +0 -1
- package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-Dg5ffKNR.js +0 -1
- package/dist/dashboard/client/assets/clone-CKI4Qu1i.js +0 -1
- package/dist/dashboard/client/assets/index-CPEavIIM.css +0 -1
- package/dist/dashboard/client/assets/index-icxlpW-l.js +0 -456
- 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(
|
|
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 =
|
|
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 && !
|
|
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}-${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
];
|
|
@@ -20187,21 +20211,30 @@ import { join } from "node:path";
|
|
|
20187
20211
|
var START_MARKER = "# OCR:START \u2014 managed by open-code-review (do not edit this block)";
|
|
20188
20212
|
var END_MARKER = "# OCR:END";
|
|
20189
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
|
+
]);
|
|
20190
20220
|
function buildManagedBlock() {
|
|
20191
20221
|
return [START_MARKER, ...MANAGED_ENTRIES, END_MARKER].join("\n");
|
|
20192
20222
|
}
|
|
20193
20223
|
function escapeRegex(str) {
|
|
20194
20224
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20195
20225
|
}
|
|
20226
|
+
function stripLegacyLines(content) {
|
|
20227
|
+
return content.split("\n").filter((line) => !LEGACY_LINES.has(line.trim())).join("\n");
|
|
20228
|
+
}
|
|
20196
20229
|
function ensureGitignore(ocrDir) {
|
|
20197
20230
|
const gitignorePath = join(ocrDir, ".gitignore");
|
|
20198
20231
|
const block = buildManagedBlock();
|
|
20199
20232
|
let content = existsSync(gitignorePath) ? readFileSync2(gitignorePath, "utf-8") : "";
|
|
20200
|
-
const
|
|
20233
|
+
const blockRegex = new RegExp(
|
|
20201
20234
|
`${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`,
|
|
20202
20235
|
"g"
|
|
20203
20236
|
);
|
|
20204
|
-
if (
|
|
20237
|
+
if (blockRegex.test(content)) {
|
|
20205
20238
|
content = content.replace(
|
|
20206
20239
|
new RegExp(
|
|
20207
20240
|
`${escapeRegex(START_MARKER)}[\\s\\S]*?${escapeRegex(END_MARKER)}\\n?`,
|
|
@@ -20210,7 +20243,7 @@ function ensureGitignore(ocrDir) {
|
|
|
20210
20243
|
block + "\n"
|
|
20211
20244
|
);
|
|
20212
20245
|
} else {
|
|
20213
|
-
content = content.trimEnd();
|
|
20246
|
+
content = stripLegacyLines(content).trimEnd();
|
|
20214
20247
|
if (content.length > 0) {
|
|
20215
20248
|
content += "\n\n";
|
|
20216
20249
|
}
|
|
@@ -20328,6 +20361,153 @@ function installCommandsForTool(tool, commandsSource, targetDir) {
|
|
|
20328
20361
|
return false;
|
|
20329
20362
|
}
|
|
20330
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
|
+
}
|
|
20331
20511
|
function installForTool(tool, targetDir) {
|
|
20332
20512
|
const agentsPath = getAgentsPackagePath();
|
|
20333
20513
|
const ocrSkillsSource = join2(agentsPath, "skills", "ocr");
|
|
@@ -20402,6 +20582,14 @@ function installForTool(tool, targetDir) {
|
|
|
20402
20582
|
}
|
|
20403
20583
|
}
|
|
20404
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
|
+
}
|
|
20405
20593
|
const commandsOk = installCommandsForTool(tool, commandsSource, targetDir);
|
|
20406
20594
|
if (!commandsOk) {
|
|
20407
20595
|
return {
|
|
@@ -24507,12 +24695,19 @@ function renderCombinedProgress(sessionPath, preservedStartTimes, ocrDir) {
|
|
|
24507
24695
|
}
|
|
24508
24696
|
|
|
24509
24697
|
// src/commands/state.ts
|
|
24510
|
-
import { existsSync as existsSync12, mkdirSync as
|
|
24698
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5 } from "node:fs";
|
|
24511
24699
|
import { join as join14 } from "node:path";
|
|
24512
24700
|
|
|
24513
24701
|
// src/lib/state/index.ts
|
|
24514
24702
|
init_db();
|
|
24515
|
-
import {
|
|
24703
|
+
import {
|
|
24704
|
+
existsSync as existsSync11,
|
|
24705
|
+
mkdirSync as mkdirSync4,
|
|
24706
|
+
readdirSync as readdirSync6,
|
|
24707
|
+
readFileSync as readFileSync9,
|
|
24708
|
+
statSync as statSync2,
|
|
24709
|
+
writeFileSync as writeFileSync7
|
|
24710
|
+
} from "node:fs";
|
|
24516
24711
|
import { join as join13 } from "node:path";
|
|
24517
24712
|
async function stateInit(params) {
|
|
24518
24713
|
const { sessionId, branch, workflowType, sessionDir, ocrDir } = params;
|
|
@@ -24669,6 +24864,253 @@ async function resolveActiveSession(ocrDir) {
|
|
|
24669
24864
|
sessionDir: session.session_dir
|
|
24670
24865
|
};
|
|
24671
24866
|
}
|
|
24867
|
+
function readJsonFromSource(params) {
|
|
24868
|
+
if (params.source === "file") {
|
|
24869
|
+
if (!existsSync11(params.filePath)) {
|
|
24870
|
+
throw new Error(`File not found: ${params.filePath}`);
|
|
24871
|
+
}
|
|
24872
|
+
return readFileSync9(params.filePath, "utf-8");
|
|
24873
|
+
}
|
|
24874
|
+
return params.data;
|
|
24875
|
+
}
|
|
24876
|
+
function parseRawJson(raw, label) {
|
|
24877
|
+
try {
|
|
24878
|
+
return JSON.parse(raw);
|
|
24879
|
+
} catch (err) {
|
|
24880
|
+
throw new Error(
|
|
24881
|
+
`Failed to parse ${label}: ${err instanceof Error ? err.message : "invalid JSON"}`
|
|
24882
|
+
);
|
|
24883
|
+
}
|
|
24884
|
+
}
|
|
24885
|
+
function resolveSessionForCompletion(db, explicitId) {
|
|
24886
|
+
if (explicitId) {
|
|
24887
|
+
const existing = getSession(db, explicitId);
|
|
24888
|
+
if (!existing) throw new Error(`Session not found: ${explicitId}`);
|
|
24889
|
+
return {
|
|
24890
|
+
id: existing.id,
|
|
24891
|
+
session_dir: existing.session_dir,
|
|
24892
|
+
current_round: existing.current_round,
|
|
24893
|
+
current_map_run: existing.current_map_run
|
|
24894
|
+
};
|
|
24895
|
+
}
|
|
24896
|
+
const active = getLatestActiveSession(db);
|
|
24897
|
+
if (!active) throw new Error("No active session found");
|
|
24898
|
+
return {
|
|
24899
|
+
id: active.id,
|
|
24900
|
+
session_dir: active.session_dir,
|
|
24901
|
+
current_round: active.current_round,
|
|
24902
|
+
current_map_run: active.current_map_run
|
|
24903
|
+
};
|
|
24904
|
+
}
|
|
24905
|
+
var VALID_CATEGORIES = /* @__PURE__ */ new Set(["blocker", "should_fix", "suggestion", "style"]);
|
|
24906
|
+
var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info"]);
|
|
24907
|
+
function validateRoundMeta(meta) {
|
|
24908
|
+
if (!meta || typeof meta !== "object") {
|
|
24909
|
+
throw new Error("round-meta.json must be a JSON object");
|
|
24910
|
+
}
|
|
24911
|
+
const obj = meta;
|
|
24912
|
+
if (obj.schema_version !== 1) {
|
|
24913
|
+
throw new Error(
|
|
24914
|
+
`Unsupported schema_version: ${String(obj.schema_version)}. Expected 1.`
|
|
24915
|
+
);
|
|
24916
|
+
}
|
|
24917
|
+
if (typeof obj.verdict !== "string" || obj.verdict.trim().length === 0) {
|
|
24918
|
+
throw new Error("round-meta.json must contain a non-empty verdict string");
|
|
24919
|
+
}
|
|
24920
|
+
if (!Array.isArray(obj.reviewers)) {
|
|
24921
|
+
throw new Error("round-meta.json must contain a reviewers array");
|
|
24922
|
+
}
|
|
24923
|
+
for (const reviewer of obj.reviewers) {
|
|
24924
|
+
if (!reviewer || typeof reviewer !== "object") {
|
|
24925
|
+
throw new Error("Each reviewer must be an object");
|
|
24926
|
+
}
|
|
24927
|
+
const r = reviewer;
|
|
24928
|
+
if (typeof r.type !== "string") {
|
|
24929
|
+
throw new Error("Each reviewer must have a type string");
|
|
24930
|
+
}
|
|
24931
|
+
if (typeof r.instance !== "number") {
|
|
24932
|
+
throw new Error("Each reviewer must have an instance number");
|
|
24933
|
+
}
|
|
24934
|
+
if (!Array.isArray(r.findings)) {
|
|
24935
|
+
throw new Error(`Reviewer ${r.type}-${r.instance} must have a findings array`);
|
|
24936
|
+
}
|
|
24937
|
+
for (const finding of r.findings) {
|
|
24938
|
+
if (!finding || typeof finding !== "object") {
|
|
24939
|
+
throw new Error("Each finding must be an object");
|
|
24940
|
+
}
|
|
24941
|
+
const f = finding;
|
|
24942
|
+
if (typeof f.title !== "string" || f.title.trim().length === 0) {
|
|
24943
|
+
throw new Error("Each finding must have a non-empty title");
|
|
24944
|
+
}
|
|
24945
|
+
if (typeof f.category !== "string" || !VALID_CATEGORIES.has(f.category)) {
|
|
24946
|
+
throw new Error(
|
|
24947
|
+
`Finding "${f.title}" has invalid category: "${String(f.category)}". Must be one of: ${[...VALID_CATEGORIES].join(", ")}`
|
|
24948
|
+
);
|
|
24949
|
+
}
|
|
24950
|
+
if (typeof f.severity !== "string" || !VALID_SEVERITIES.has(f.severity)) {
|
|
24951
|
+
throw new Error(
|
|
24952
|
+
`Finding "${f.title}" has invalid severity: "${String(f.severity)}". Must be one of: ${[...VALID_SEVERITIES].join(", ")}`
|
|
24953
|
+
);
|
|
24954
|
+
}
|
|
24955
|
+
if (typeof f.summary !== "string") {
|
|
24956
|
+
throw new Error(`Finding "${f.title}" must have a summary string`);
|
|
24957
|
+
}
|
|
24958
|
+
if (f.file_path !== void 0 && typeof f.file_path !== "string") {
|
|
24959
|
+
throw new Error(`Finding "${f.title}" has invalid file_path: expected string`);
|
|
24960
|
+
}
|
|
24961
|
+
if (f.line_start !== void 0 && typeof f.line_start !== "number") {
|
|
24962
|
+
throw new Error(`Finding "${f.title}" has invalid line_start: expected number`);
|
|
24963
|
+
}
|
|
24964
|
+
if (f.line_end !== void 0 && typeof f.line_end !== "number") {
|
|
24965
|
+
throw new Error(`Finding "${f.title}" has invalid line_end: expected number`);
|
|
24966
|
+
}
|
|
24967
|
+
if (f.flagged_by !== void 0 && !Array.isArray(f.flagged_by)) {
|
|
24968
|
+
throw new Error(`Finding "${f.title}" has invalid flagged_by: expected array`);
|
|
24969
|
+
}
|
|
24970
|
+
}
|
|
24971
|
+
}
|
|
24972
|
+
return meta;
|
|
24973
|
+
}
|
|
24974
|
+
function computeRoundCounts(meta) {
|
|
24975
|
+
const allFindings = [];
|
|
24976
|
+
for (const reviewer of meta.reviewers) {
|
|
24977
|
+
allFindings.push(...reviewer.findings);
|
|
24978
|
+
}
|
|
24979
|
+
return {
|
|
24980
|
+
blockerCount: allFindings.filter((f) => f.category === "blocker").length,
|
|
24981
|
+
shouldFixCount: allFindings.filter((f) => f.category === "should_fix").length,
|
|
24982
|
+
suggestionCount: allFindings.filter((f) => f.category === "suggestion").length,
|
|
24983
|
+
reviewerCount: meta.reviewers.length,
|
|
24984
|
+
totalFindingCount: allFindings.length
|
|
24985
|
+
};
|
|
24986
|
+
}
|
|
24987
|
+
async function stateRoundComplete(params) {
|
|
24988
|
+
const { ocrDir } = params;
|
|
24989
|
+
const db = await ensureDatabase(ocrDir);
|
|
24990
|
+
const dbPath = join13(ocrDir, "data", "ocr.db");
|
|
24991
|
+
const rawJsonString = readJsonFromSource(params);
|
|
24992
|
+
const label = params.source === "file" ? params.filePath : "stdin";
|
|
24993
|
+
const raw = parseRawJson(rawJsonString, label);
|
|
24994
|
+
const meta = validateRoundMeta(raw);
|
|
24995
|
+
const counts = computeRoundCounts(meta);
|
|
24996
|
+
const session = resolveSessionForCompletion(db, params.sessionId);
|
|
24997
|
+
const roundNumber = params.round ?? session.current_round;
|
|
24998
|
+
let metaPath;
|
|
24999
|
+
if (params.source === "stdin") {
|
|
25000
|
+
const roundDir = join13(session.session_dir, "rounds", `round-${roundNumber}`);
|
|
25001
|
+
mkdirSync4(roundDir, { recursive: true });
|
|
25002
|
+
metaPath = join13(roundDir, "round-meta.json");
|
|
25003
|
+
writeFileSync7(metaPath, JSON.stringify(meta, null, 2));
|
|
25004
|
+
}
|
|
25005
|
+
insertEvent(db, {
|
|
25006
|
+
session_id: session.id,
|
|
25007
|
+
event_type: "round_completed",
|
|
25008
|
+
phase: "synthesis",
|
|
25009
|
+
phase_number: 7,
|
|
25010
|
+
round: roundNumber,
|
|
25011
|
+
metadata: JSON.stringify({
|
|
25012
|
+
verdict: meta.verdict,
|
|
25013
|
+
blocker_count: counts.blockerCount,
|
|
25014
|
+
should_fix_count: counts.shouldFixCount,
|
|
25015
|
+
suggestion_count: counts.suggestionCount,
|
|
25016
|
+
reviewer_count: counts.reviewerCount,
|
|
25017
|
+
total_finding_count: counts.totalFindingCount,
|
|
25018
|
+
source: "orchestrator"
|
|
25019
|
+
})
|
|
25020
|
+
});
|
|
25021
|
+
saveDatabase(db, dbPath);
|
|
25022
|
+
return { sessionId: session.id, round: roundNumber, metaPath };
|
|
25023
|
+
}
|
|
25024
|
+
function validateMapMeta(meta) {
|
|
25025
|
+
if (!meta || typeof meta !== "object") {
|
|
25026
|
+
throw new Error("map-meta.json must be a JSON object");
|
|
25027
|
+
}
|
|
25028
|
+
const obj = meta;
|
|
25029
|
+
if (obj.schema_version !== 1) {
|
|
25030
|
+
throw new Error(
|
|
25031
|
+
`Unsupported schema_version: ${String(obj.schema_version)}. Expected 1.`
|
|
25032
|
+
);
|
|
25033
|
+
}
|
|
25034
|
+
if (!Array.isArray(obj.sections)) {
|
|
25035
|
+
throw new Error("map-meta.json must contain a sections array");
|
|
25036
|
+
}
|
|
25037
|
+
for (const section of obj.sections) {
|
|
25038
|
+
if (!section || typeof section !== "object") {
|
|
25039
|
+
throw new Error("Each section must be an object");
|
|
25040
|
+
}
|
|
25041
|
+
const s = section;
|
|
25042
|
+
if (typeof s.section_number !== "number") {
|
|
25043
|
+
throw new Error("Each section must have a section_number");
|
|
25044
|
+
}
|
|
25045
|
+
if (typeof s.title !== "string" || s.title.trim().length === 0) {
|
|
25046
|
+
throw new Error("Each section must have a non-empty title");
|
|
25047
|
+
}
|
|
25048
|
+
if (!Array.isArray(s.files)) {
|
|
25049
|
+
throw new Error(`Section "${s.title}" must have a files array`);
|
|
25050
|
+
}
|
|
25051
|
+
for (const file of s.files) {
|
|
25052
|
+
if (!file || typeof file !== "object") {
|
|
25053
|
+
throw new Error("Each file must be an object");
|
|
25054
|
+
}
|
|
25055
|
+
const f = file;
|
|
25056
|
+
if (typeof f.file_path !== "string" || f.file_path.trim().length === 0) {
|
|
25057
|
+
throw new Error("Each file must have a non-empty file_path");
|
|
25058
|
+
}
|
|
25059
|
+
if (typeof f.role !== "string") {
|
|
25060
|
+
throw new Error(`File "${f.file_path}" must have a role string`);
|
|
25061
|
+
}
|
|
25062
|
+
if (typeof f.lines_added !== "number") {
|
|
25063
|
+
throw new Error(`File "${f.file_path}" must have a lines_added number`);
|
|
25064
|
+
}
|
|
25065
|
+
if (typeof f.lines_deleted !== "number") {
|
|
25066
|
+
throw new Error(`File "${f.file_path}" must have a lines_deleted number`);
|
|
25067
|
+
}
|
|
25068
|
+
}
|
|
25069
|
+
}
|
|
25070
|
+
if (obj.dependencies !== void 0 && !Array.isArray(obj.dependencies)) {
|
|
25071
|
+
throw new Error("map-meta.json dependencies must be an array if provided");
|
|
25072
|
+
}
|
|
25073
|
+
return meta;
|
|
25074
|
+
}
|
|
25075
|
+
function computeMapCounts(meta) {
|
|
25076
|
+
return {
|
|
25077
|
+
sectionCount: meta.sections.length,
|
|
25078
|
+
fileCount: meta.sections.reduce((sum, s) => sum + s.files.length, 0)
|
|
25079
|
+
};
|
|
25080
|
+
}
|
|
25081
|
+
async function stateMapComplete(params) {
|
|
25082
|
+
const { ocrDir } = params;
|
|
25083
|
+
const db = await ensureDatabase(ocrDir);
|
|
25084
|
+
const dbPath = join13(ocrDir, "data", "ocr.db");
|
|
25085
|
+
const rawJsonString = readJsonFromSource(params);
|
|
25086
|
+
const label = params.source === "file" ? params.filePath : "stdin";
|
|
25087
|
+
const raw = parseRawJson(rawJsonString, label);
|
|
25088
|
+
const meta = validateMapMeta(raw);
|
|
25089
|
+
const counts = computeMapCounts(meta);
|
|
25090
|
+
const session = resolveSessionForCompletion(db, params.sessionId);
|
|
25091
|
+
const mapRunNumber = params.mapRun ?? session.current_map_run;
|
|
25092
|
+
let metaPath;
|
|
25093
|
+
if (params.source === "stdin") {
|
|
25094
|
+
const runDir = join13(session.session_dir, "map", "runs", `run-${mapRunNumber}`);
|
|
25095
|
+
mkdirSync4(runDir, { recursive: true });
|
|
25096
|
+
metaPath = join13(runDir, "map-meta.json");
|
|
25097
|
+
writeFileSync7(metaPath, JSON.stringify(meta, null, 2));
|
|
25098
|
+
}
|
|
25099
|
+
insertEvent(db, {
|
|
25100
|
+
session_id: session.id,
|
|
25101
|
+
event_type: "map_completed",
|
|
25102
|
+
phase: "synthesis",
|
|
25103
|
+
phase_number: 5,
|
|
25104
|
+
round: mapRunNumber,
|
|
25105
|
+
metadata: JSON.stringify({
|
|
25106
|
+
section_count: counts.sectionCount,
|
|
25107
|
+
file_count: counts.fileCount,
|
|
25108
|
+
source: "orchestrator"
|
|
25109
|
+
})
|
|
25110
|
+
});
|
|
25111
|
+
saveDatabase(db, dbPath);
|
|
25112
|
+
return { sessionId: session.id, mapRun: mapRunNumber, metaPath };
|
|
25113
|
+
}
|
|
24672
25114
|
async function stateSync(ocrDir) {
|
|
24673
25115
|
const db = await ensureDatabase(ocrDir);
|
|
24674
25116
|
const dbPath = join13(ocrDir, "data", "ocr.db");
|
|
@@ -24718,6 +25160,17 @@ async function stateSync(ocrDir) {
|
|
|
24718
25160
|
}
|
|
24719
25161
|
|
|
24720
25162
|
// src/commands/state.ts
|
|
25163
|
+
async function readStdin() {
|
|
25164
|
+
const chunks = [];
|
|
25165
|
+
for await (const chunk of process.stdin) {
|
|
25166
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
25167
|
+
}
|
|
25168
|
+
const data = Buffer.concat(chunks).toString("utf-8").trim();
|
|
25169
|
+
if (data.length === 0) {
|
|
25170
|
+
throw new Error("No data received on stdin");
|
|
25171
|
+
}
|
|
25172
|
+
return data;
|
|
25173
|
+
}
|
|
24721
25174
|
var initSubcommand = new Command("init").description("Initialize a new OCR session").requiredOption("--session-id <id>", "Session ID").requiredOption("--branch <branch>", "Branch name").requiredOption(
|
|
24722
25175
|
"--workflow-type <type>",
|
|
24723
25176
|
"Workflow type (review or map)",
|
|
@@ -24736,7 +25189,7 @@ var initSubcommand = new Command("init").description("Initialize a new OCR sessi
|
|
|
24736
25189
|
const ocrDir = join14(targetDir, ".ocr");
|
|
24737
25190
|
const sessionDir = options.sessionDir ?? join14(ocrDir, "sessions", options.sessionId);
|
|
24738
25191
|
if (!existsSync12(sessionDir)) {
|
|
24739
|
-
|
|
25192
|
+
mkdirSync5(sessionDir, { recursive: true });
|
|
24740
25193
|
}
|
|
24741
25194
|
try {
|
|
24742
25195
|
const sessionId = await stateInit({
|
|
@@ -24762,6 +25215,23 @@ var transitionSubcommand = new Command("transition").description("Transition ses
|
|
|
24762
25215
|
const targetDir = process.cwd();
|
|
24763
25216
|
requireOcrSetup(targetDir);
|
|
24764
25217
|
const ocrDir = join14(targetDir, ".ocr");
|
|
25218
|
+
const VALID_PHASES = /* @__PURE__ */ new Set([
|
|
25219
|
+
"context",
|
|
25220
|
+
"change-context",
|
|
25221
|
+
"analysis",
|
|
25222
|
+
"reviews",
|
|
25223
|
+
"aggregation",
|
|
25224
|
+
"discourse",
|
|
25225
|
+
"synthesis",
|
|
25226
|
+
"complete",
|
|
25227
|
+
"map-context",
|
|
25228
|
+
"topology",
|
|
25229
|
+
"flow-analysis",
|
|
25230
|
+
"requirements-mapping"
|
|
25231
|
+
]);
|
|
25232
|
+
if (!VALID_PHASES.has(options.phase)) {
|
|
25233
|
+
throw new Error(`Invalid phase: "${options.phase}". Must be one of: ${[...VALID_PHASES].join(", ")}`);
|
|
25234
|
+
}
|
|
24765
25235
|
try {
|
|
24766
25236
|
const sessionId = options.sessionId ?? (await resolveActiveSession(ocrDir)).id;
|
|
24767
25237
|
await stateTransition({
|
|
@@ -24890,7 +25360,105 @@ var syncSubcommand = new Command("sync").description("Rebuild session state from
|
|
|
24890
25360
|
process.exit(1);
|
|
24891
25361
|
}
|
|
24892
25362
|
});
|
|
24893
|
-
var
|
|
25363
|
+
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(
|
|
25364
|
+
async (options) => {
|
|
25365
|
+
const targetDir = process.cwd();
|
|
25366
|
+
requireOcrSetup(targetDir);
|
|
25367
|
+
const ocrDir = join14(targetDir, ".ocr");
|
|
25368
|
+
if (!options.file && !options.stdin) {
|
|
25369
|
+
console.error(source_default.red("Error: Provide either --file <path> or --stdin"));
|
|
25370
|
+
process.exit(1);
|
|
25371
|
+
}
|
|
25372
|
+
if (options.file && options.stdin) {
|
|
25373
|
+
console.error(source_default.red("Error: --file and --stdin are mutually exclusive"));
|
|
25374
|
+
process.exit(1);
|
|
25375
|
+
}
|
|
25376
|
+
try {
|
|
25377
|
+
let result;
|
|
25378
|
+
if (options.stdin) {
|
|
25379
|
+
const data = await readStdin();
|
|
25380
|
+
result = await stateRoundComplete({
|
|
25381
|
+
source: "stdin",
|
|
25382
|
+
ocrDir,
|
|
25383
|
+
data,
|
|
25384
|
+
sessionId: options.sessionId,
|
|
25385
|
+
round: options.round
|
|
25386
|
+
});
|
|
25387
|
+
} else if (options.file) {
|
|
25388
|
+
result = await stateRoundComplete({
|
|
25389
|
+
source: "file",
|
|
25390
|
+
ocrDir,
|
|
25391
|
+
filePath: options.file,
|
|
25392
|
+
sessionId: options.sessionId,
|
|
25393
|
+
round: options.round
|
|
25394
|
+
});
|
|
25395
|
+
} else {
|
|
25396
|
+
process.exit(1);
|
|
25397
|
+
}
|
|
25398
|
+
console.log(source_default.green("Round data imported successfully."));
|
|
25399
|
+
if (result.metaPath) {
|
|
25400
|
+
console.log(source_default.dim(`Wrote ${result.metaPath}`));
|
|
25401
|
+
}
|
|
25402
|
+
} catch (error) {
|
|
25403
|
+
console.error(
|
|
25404
|
+
source_default.red(
|
|
25405
|
+
`Error: ${error instanceof Error ? error.message : "Failed to import round data"}`
|
|
25406
|
+
)
|
|
25407
|
+
);
|
|
25408
|
+
process.exit(1);
|
|
25409
|
+
}
|
|
25410
|
+
}
|
|
25411
|
+
);
|
|
25412
|
+
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(
|
|
25413
|
+
async (options) => {
|
|
25414
|
+
const targetDir = process.cwd();
|
|
25415
|
+
requireOcrSetup(targetDir);
|
|
25416
|
+
const ocrDir = join14(targetDir, ".ocr");
|
|
25417
|
+
if (!options.file && !options.stdin) {
|
|
25418
|
+
console.error(source_default.red("Error: Provide either --file <path> or --stdin"));
|
|
25419
|
+
process.exit(1);
|
|
25420
|
+
}
|
|
25421
|
+
if (options.file && options.stdin) {
|
|
25422
|
+
console.error(source_default.red("Error: --file and --stdin are mutually exclusive"));
|
|
25423
|
+
process.exit(1);
|
|
25424
|
+
}
|
|
25425
|
+
try {
|
|
25426
|
+
let result;
|
|
25427
|
+
if (options.stdin) {
|
|
25428
|
+
const data = await readStdin();
|
|
25429
|
+
result = await stateMapComplete({
|
|
25430
|
+
source: "stdin",
|
|
25431
|
+
ocrDir,
|
|
25432
|
+
data,
|
|
25433
|
+
sessionId: options.sessionId,
|
|
25434
|
+
mapRun: options.mapRun
|
|
25435
|
+
});
|
|
25436
|
+
} else if (options.file) {
|
|
25437
|
+
result = await stateMapComplete({
|
|
25438
|
+
source: "file",
|
|
25439
|
+
ocrDir,
|
|
25440
|
+
filePath: options.file,
|
|
25441
|
+
sessionId: options.sessionId,
|
|
25442
|
+
mapRun: options.mapRun
|
|
25443
|
+
});
|
|
25444
|
+
} else {
|
|
25445
|
+
process.exit(1);
|
|
25446
|
+
}
|
|
25447
|
+
console.log(source_default.green("Map data imported successfully."));
|
|
25448
|
+
if (result.metaPath) {
|
|
25449
|
+
console.log(source_default.dim(`Wrote ${result.metaPath}`));
|
|
25450
|
+
}
|
|
25451
|
+
} catch (error) {
|
|
25452
|
+
console.error(
|
|
25453
|
+
source_default.red(
|
|
25454
|
+
`Error: ${error instanceof Error ? error.message : "Failed to import map data"}`
|
|
25455
|
+
)
|
|
25456
|
+
);
|
|
25457
|
+
process.exit(1);
|
|
25458
|
+
}
|
|
25459
|
+
}
|
|
25460
|
+
);
|
|
25461
|
+
var stateCommand = new Command("state").description("Manage OCR session state").addCommand(initSubcommand).addCommand(transitionSubcommand).addCommand(closeSubcommand).addCommand(showSubcommand).addCommand(syncSubcommand).addCommand(roundCompleteSubcommand).addCommand(mapCompleteSubcommand);
|
|
24894
25462
|
|
|
24895
25463
|
// src/commands/update.ts
|
|
24896
25464
|
import { existsSync as existsSync13 } from "node:fs";
|
|
@@ -25203,8 +25771,225 @@ var doctorCommand = new Command("doctor").description("Check OCR installation an
|
|
|
25203
25771
|
console.log();
|
|
25204
25772
|
});
|
|
25205
25773
|
|
|
25774
|
+
// src/commands/reviewers.ts
|
|
25775
|
+
import { writeFileSync as writeFileSync8, renameSync as renameSync2 } from "node:fs";
|
|
25776
|
+
import { join as join18 } from "node:path";
|
|
25777
|
+
async function readStdin2() {
|
|
25778
|
+
const chunks = [];
|
|
25779
|
+
for await (const chunk of process.stdin) {
|
|
25780
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
25781
|
+
}
|
|
25782
|
+
const data = Buffer.concat(chunks).toString("utf-8").trim();
|
|
25783
|
+
if (data.length === 0) {
|
|
25784
|
+
throw new Error("No data received on stdin");
|
|
25785
|
+
}
|
|
25786
|
+
return data;
|
|
25787
|
+
}
|
|
25788
|
+
var VALID_TIERS = /* @__PURE__ */ new Set(["holistic", "specialist", "persona", "custom"]);
|
|
25789
|
+
var SLUG_RE = /^[a-z][a-z0-9-]*$/;
|
|
25790
|
+
function validateReviewersMeta(data) {
|
|
25791
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
25792
|
+
throw new Error("Payload must be a JSON object");
|
|
25793
|
+
}
|
|
25794
|
+
const obj = data;
|
|
25795
|
+
if (obj.schema_version !== 1) {
|
|
25796
|
+
throw new Error(`schema_version must be 1, got ${JSON.stringify(obj.schema_version)}`);
|
|
25797
|
+
}
|
|
25798
|
+
if (typeof obj.generated_at !== "string" || obj.generated_at.length === 0) {
|
|
25799
|
+
throw new Error("generated_at must be a non-empty ISO 8601 string");
|
|
25800
|
+
}
|
|
25801
|
+
if (!Array.isArray(obj.reviewers)) {
|
|
25802
|
+
throw new Error("reviewers must be an array");
|
|
25803
|
+
}
|
|
25804
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
25805
|
+
for (let i = 0; i < obj.reviewers.length; i++) {
|
|
25806
|
+
const r = obj.reviewers[i];
|
|
25807
|
+
const prefix = `reviewers[${i}]`;
|
|
25808
|
+
if (typeof r.id !== "string" || !SLUG_RE.test(r.id)) {
|
|
25809
|
+
throw new Error(`${prefix}.id must be a lowercase slug (got ${JSON.stringify(r.id)})`);
|
|
25810
|
+
}
|
|
25811
|
+
if (seenIds.has(r.id)) {
|
|
25812
|
+
throw new Error(`Duplicate reviewer id: "${r.id}"`);
|
|
25813
|
+
}
|
|
25814
|
+
seenIds.add(r.id);
|
|
25815
|
+
if (typeof r.name !== "string" || r.name.length === 0) {
|
|
25816
|
+
throw new Error(`${prefix}.name must be a non-empty string`);
|
|
25817
|
+
}
|
|
25818
|
+
if (!VALID_TIERS.has(r.tier)) {
|
|
25819
|
+
throw new Error(`${prefix}.tier must be one of: ${[...VALID_TIERS].join(", ")} (got ${JSON.stringify(r.tier)})`);
|
|
25820
|
+
}
|
|
25821
|
+
if (typeof r.description !== "string" || r.description.length === 0) {
|
|
25822
|
+
throw new Error(`${prefix}.description must be a non-empty string`);
|
|
25823
|
+
}
|
|
25824
|
+
if (!Array.isArray(r.focus_areas)) {
|
|
25825
|
+
throw new Error(`${prefix}.focus_areas must be an array`);
|
|
25826
|
+
}
|
|
25827
|
+
if (r.known_for !== void 0 && typeof r.known_for !== "string") {
|
|
25828
|
+
throw new Error(`${prefix}.known_for must be a string if provided`);
|
|
25829
|
+
}
|
|
25830
|
+
if (r.philosophy !== void 0 && typeof r.philosophy !== "string") {
|
|
25831
|
+
throw new Error(`${prefix}.philosophy must be a string if provided`);
|
|
25832
|
+
}
|
|
25833
|
+
}
|
|
25834
|
+
return data;
|
|
25835
|
+
}
|
|
25836
|
+
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) => {
|
|
25837
|
+
const targetDir = process.cwd();
|
|
25838
|
+
requireOcrSetup(targetDir);
|
|
25839
|
+
const ocrDir = join18(targetDir, ".ocr");
|
|
25840
|
+
if (!options.stdin) {
|
|
25841
|
+
try {
|
|
25842
|
+
const reviewersDir = join18(ocrDir, "skills", "references", "reviewers");
|
|
25843
|
+
const configPath = join18(ocrDir, "config.yaml");
|
|
25844
|
+
const meta = generateReviewersMeta(reviewersDir, configPath);
|
|
25845
|
+
if (!meta || meta.reviewers.length === 0) {
|
|
25846
|
+
console.error(source_default.yellow("No reviewer files found in .ocr/skills/references/reviewers/"));
|
|
25847
|
+
process.exit(1);
|
|
25848
|
+
}
|
|
25849
|
+
const metaPath = join18(ocrDir, "reviewers-meta.json");
|
|
25850
|
+
const tmpPath = metaPath + ".tmp";
|
|
25851
|
+
writeFileSync8(tmpPath, JSON.stringify(meta, null, 2) + "\n");
|
|
25852
|
+
renameSync2(tmpPath, metaPath);
|
|
25853
|
+
const tierCounts = meta.reviewers.reduce(
|
|
25854
|
+
(acc, r) => {
|
|
25855
|
+
acc[r.tier] = (acc[r.tier] ?? 0) + 1;
|
|
25856
|
+
return acc;
|
|
25857
|
+
},
|
|
25858
|
+
{}
|
|
25859
|
+
);
|
|
25860
|
+
const breakdown = Object.entries(tierCounts).map(([tier, count]) => `${count} ${tier}`).join(", ");
|
|
25861
|
+
console.log(source_default.green(`Synced ${meta.reviewers.length} reviewer(s) (${breakdown}).`));
|
|
25862
|
+
} catch (error) {
|
|
25863
|
+
console.error(
|
|
25864
|
+
source_default.red(`Error: ${error instanceof Error ? error.message : "Failed to sync reviewers"}`)
|
|
25865
|
+
);
|
|
25866
|
+
process.exit(1);
|
|
25867
|
+
}
|
|
25868
|
+
return;
|
|
25869
|
+
}
|
|
25870
|
+
try {
|
|
25871
|
+
const raw = await readStdin2();
|
|
25872
|
+
let parsed;
|
|
25873
|
+
try {
|
|
25874
|
+
parsed = JSON.parse(raw);
|
|
25875
|
+
} catch {
|
|
25876
|
+
throw new Error("Invalid JSON on stdin");
|
|
25877
|
+
}
|
|
25878
|
+
const meta = validateReviewersMeta(parsed);
|
|
25879
|
+
const metaPath = join18(ocrDir, "reviewers-meta.json");
|
|
25880
|
+
const tmpPath = metaPath + ".tmp";
|
|
25881
|
+
writeFileSync8(tmpPath, JSON.stringify(meta, null, 2) + "\n");
|
|
25882
|
+
renameSync2(tmpPath, metaPath);
|
|
25883
|
+
const tierCounts = meta.reviewers.reduce(
|
|
25884
|
+
(acc, r) => {
|
|
25885
|
+
acc[r.tier] = (acc[r.tier] ?? 0) + 1;
|
|
25886
|
+
return acc;
|
|
25887
|
+
},
|
|
25888
|
+
{}
|
|
25889
|
+
);
|
|
25890
|
+
const breakdown = Object.entries(tierCounts).map(([tier, count]) => `${count} ${tier}`).join(", ");
|
|
25891
|
+
console.log(
|
|
25892
|
+
source_default.green(`Synced ${meta.reviewers.length} reviewer(s) (${breakdown}).`)
|
|
25893
|
+
);
|
|
25894
|
+
} catch (error) {
|
|
25895
|
+
console.error(
|
|
25896
|
+
source_default.red(
|
|
25897
|
+
`Error: ${error instanceof Error ? error.message : "Failed to sync reviewers"}`
|
|
25898
|
+
)
|
|
25899
|
+
);
|
|
25900
|
+
process.exit(1);
|
|
25901
|
+
}
|
|
25902
|
+
});
|
|
25903
|
+
var reviewersCommand = new Command("reviewers").description("Manage OCR reviewer metadata").addCommand(syncSubcommand2);
|
|
25904
|
+
|
|
25905
|
+
// src/lib/update-check.ts
|
|
25906
|
+
import { homedir } from "node:os";
|
|
25907
|
+
import { join as join19 } from "node:path";
|
|
25908
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, mkdirSync as mkdirSync6 } from "node:fs";
|
|
25909
|
+
var PACKAGE_NAME = "@open-code-review/cli";
|
|
25910
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
25911
|
+
var CACHE_DIR = join19(homedir(), ".ocr");
|
|
25912
|
+
var CACHE_FILE = join19(CACHE_DIR, "update-check.json");
|
|
25913
|
+
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
25914
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
25915
|
+
function readCache(cacheFile) {
|
|
25916
|
+
try {
|
|
25917
|
+
return JSON.parse(readFileSync10(cacheFile, "utf-8"));
|
|
25918
|
+
} catch {
|
|
25919
|
+
return null;
|
|
25920
|
+
}
|
|
25921
|
+
}
|
|
25922
|
+
function writeCache(cacheFile, cache) {
|
|
25923
|
+
try {
|
|
25924
|
+
mkdirSync6(join19(cacheFile, ".."), { recursive: true });
|
|
25925
|
+
writeFileSync9(cacheFile, JSON.stringify(cache));
|
|
25926
|
+
} catch {
|
|
25927
|
+
}
|
|
25928
|
+
}
|
|
25929
|
+
function isNewer(latest, current) {
|
|
25930
|
+
const l = latest.split(".").map(Number);
|
|
25931
|
+
const c = current.split(".").map(Number);
|
|
25932
|
+
for (let i = 0; i < 3; i++) {
|
|
25933
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
25934
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
25935
|
+
}
|
|
25936
|
+
return false;
|
|
25937
|
+
}
|
|
25938
|
+
function detectUpdateCommand() {
|
|
25939
|
+
return `npm i -g ${PACKAGE_NAME}@latest && ocr update`;
|
|
25940
|
+
}
|
|
25941
|
+
async function checkForUpdate(currentVersion, options) {
|
|
25942
|
+
if (process.env.CI || process.env.OCR_NO_UPDATE_CHECK) {
|
|
25943
|
+
return null;
|
|
25944
|
+
}
|
|
25945
|
+
const cacheFile = join19(options?.cacheDir ?? CACHE_DIR, "update-check.json");
|
|
25946
|
+
try {
|
|
25947
|
+
const cache = readCache(cacheFile);
|
|
25948
|
+
if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
|
|
25949
|
+
if (!cache.latestVersion) return null;
|
|
25950
|
+
if (!isNewer(cache.latestVersion, currentVersion)) return null;
|
|
25951
|
+
return {
|
|
25952
|
+
updateAvailable: true,
|
|
25953
|
+
currentVersion,
|
|
25954
|
+
latestVersion: cache.latestVersion,
|
|
25955
|
+
updateCommand: detectUpdateCommand()
|
|
25956
|
+
};
|
|
25957
|
+
}
|
|
25958
|
+
const response = await fetch(REGISTRY_URL, {
|
|
25959
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
25960
|
+
});
|
|
25961
|
+
const data = await response.json();
|
|
25962
|
+
const latestVersion = data.version ?? null;
|
|
25963
|
+
writeCache(cacheFile, { lastCheck: Date.now(), latestVersion });
|
|
25964
|
+
if (!latestVersion || !isNewer(latestVersion, currentVersion)) {
|
|
25965
|
+
return null;
|
|
25966
|
+
}
|
|
25967
|
+
return {
|
|
25968
|
+
updateAvailable: true,
|
|
25969
|
+
currentVersion,
|
|
25970
|
+
latestVersion,
|
|
25971
|
+
updateCommand: detectUpdateCommand()
|
|
25972
|
+
};
|
|
25973
|
+
} catch {
|
|
25974
|
+
writeCache(cacheFile, { lastCheck: Date.now(), latestVersion: null });
|
|
25975
|
+
return null;
|
|
25976
|
+
}
|
|
25977
|
+
}
|
|
25978
|
+
function printUpdateNotification(result) {
|
|
25979
|
+
const line1 = source_default.yellow(" Update available: ") + source_default.dim(result.currentVersion) + source_default.yellow(" \u2192 ") + source_default.green(result.latestVersion);
|
|
25980
|
+
const line2 = source_default.dim(" Run: ") + source_default.bold(result.updateCommand);
|
|
25981
|
+
process.stderr.write(`
|
|
25982
|
+
${line1}
|
|
25983
|
+
${line2}
|
|
25984
|
+
|
|
25985
|
+
`);
|
|
25986
|
+
}
|
|
25987
|
+
|
|
25206
25988
|
// src/index.ts
|
|
25207
|
-
var cliVersion = true ? "1.
|
|
25989
|
+
var cliVersion = true ? "1.7.0" : createRequire(import.meta.url)("../package.json").version;
|
|
25990
|
+
var HUMAN_COMMANDS = /* @__PURE__ */ new Set(["init", "update", "doctor", "dashboard", "progress"]);
|
|
25991
|
+
var subcommand = process.argv[2];
|
|
25992
|
+
var updateCheck = subcommand && HUMAN_COMMANDS.has(subcommand) ? checkForUpdate(cliVersion) : null;
|
|
25208
25993
|
var program2 = new Command();
|
|
25209
25994
|
program2.name("ocr").description("Open Code Review - AI-powered multi-agent code review").version(cliVersion);
|
|
25210
25995
|
program2.addCommand(initCommand);
|
|
@@ -25213,7 +25998,17 @@ program2.addCommand(stateCommand);
|
|
|
25213
25998
|
program2.addCommand(updateCommand);
|
|
25214
25999
|
program2.addCommand(dashboardCommand);
|
|
25215
26000
|
program2.addCommand(doctorCommand);
|
|
25216
|
-
program2.
|
|
26001
|
+
program2.addCommand(reviewersCommand);
|
|
26002
|
+
await program2.parseAsync();
|
|
26003
|
+
if (updateCheck) {
|
|
26004
|
+
const updateResult = await Promise.race([
|
|
26005
|
+
updateCheck,
|
|
26006
|
+
new Promise((r) => setTimeout(() => r(null), 500))
|
|
26007
|
+
]);
|
|
26008
|
+
if (updateResult?.updateAvailable) {
|
|
26009
|
+
printUpdateNotification(updateResult);
|
|
26010
|
+
}
|
|
26011
|
+
}
|
|
25217
26012
|
/*! Bundled license information:
|
|
25218
26013
|
|
|
25219
26014
|
chokidar/esm/index.js:
|