@open-code-review/cli 1.6.0 → 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 +20 -4
- package/dist/dashboard/client/assets/{_basePickBy-BGuMbEDR.js → _basePickBy-DbLJVCA4.js} +1 -1
- package/dist/dashboard/client/assets/{_baseUniq-Bx8loabg.js → _baseUniq-IXEG0cJJ.js} +1 -1
- package/dist/dashboard/client/assets/{arc-DUgpt7nY.js → arc-lsKxmOJY.js} +1 -1
- package/dist/dashboard/client/assets/{architectureDiagram-VXUJARFQ-D25nt6Xz.js → architectureDiagram-VXUJARFQ-DfMlzFJX.js} +1 -1
- package/dist/dashboard/client/assets/{blockDiagram-VD42YOAC-D8PUF3h4.js → blockDiagram-VD42YOAC-bSpnd26J.js} +1 -1
- package/dist/dashboard/client/assets/{c4Diagram-YG6GDRKO-lorsCz-I.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-8lVyfRJM.js → chunk-4BX2VUAB-CI9zC4lV.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-55IACEB6-C4SjgsZO.js → chunk-55IACEB6-BqUdJdx5.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-B4BG7PRW-BXzTPbH1.js → chunk-B4BG7PRW-DymQrTp-.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-DI55MBZ5-Bp7QllDt.js → chunk-DI55MBZ5-lZ_9LKGJ.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-FMBD7UC4-B4g9S67N.js → chunk-FMBD7UC4-DC5rgLNm.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-QN33PNHL-Dyk7Hc0J.js → chunk-QN33PNHL-BrygpHrX.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-QZHKN3VN-DTvkGdnm.js → chunk-QZHKN3VN-CWJqBdNg.js} +1 -1
- package/dist/dashboard/client/assets/{chunk-TZMSLE5B-BAeZLvrI.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--6-kzrdu.js → cose-bilkent-S5V4N54A-BYvGIfo0.js} +1 -1
- package/dist/dashboard/client/assets/{dagre-6UL2VRFP-D10_QE2P.js → dagre-6UL2VRFP-B1rZyiLJ.js} +1 -1
- package/dist/dashboard/client/assets/{diagram-PSM6KHXK-kS1x75Bl.js → diagram-PSM6KHXK-Dvl5dQMd.js} +1 -1
- package/dist/dashboard/client/assets/{diagram-QEK2KX5R-D_LLCPas.js → diagram-QEK2KX5R-Cmntmhht.js} +1 -1
- package/dist/dashboard/client/assets/{diagram-S2PKOQOG-Duy1t5UO.js → diagram-S2PKOQOG-BqZcpG85.js} +1 -1
- package/dist/dashboard/client/assets/{erDiagram-Q2GNP2WA-DyQXwzLf.js → erDiagram-Q2GNP2WA-Cw7BALso.js} +1 -1
- package/dist/dashboard/client/assets/{flowDiagram-NV44I4VS-D9U11XVw.js → flowDiagram-NV44I4VS-B_amTHzQ.js} +1 -1
- package/dist/dashboard/client/assets/{ganttDiagram-JELNMOA3-STy-TC-3.js → ganttDiagram-JELNMOA3-B1j2-sTo.js} +1 -1
- package/dist/dashboard/client/assets/{gitGraphDiagram-V2S2FVAM-B04PgURg.js → gitGraphDiagram-V2S2FVAM-D5BkfAMt.js} +1 -1
- package/dist/dashboard/client/assets/{graph-AiGwnT5H.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-D4arwl6T.js → infoDiagram-HS3SLOUP-C4dtIkj3.js} +1 -1
- package/dist/dashboard/client/assets/{journeyDiagram-XKPGCS4Q-CsKqlKkf.js → journeyDiagram-XKPGCS4Q-hha4Am8v.js} +1 -1
- package/dist/dashboard/client/assets/{kanban-definition-3W4ZIXB7-CUFnzQE3.js → kanban-definition-3W4ZIXB7-1EY8l7Ng.js} +1 -1
- package/dist/dashboard/client/assets/{layout-BvvYJVPv.js → layout-7SmAbjFT.js} +1 -1
- package/dist/dashboard/client/assets/{linear-BiBJkzyE.js → linear-BfjSBezh.js} +1 -1
- package/dist/dashboard/client/assets/{mermaid-renderer-DGUmIWXY.js → mermaid-renderer-PPIt-kY4.js} +4 -4
- package/dist/dashboard/client/assets/{mindmap-definition-VGOIOE7T-D-Kc9Xgu.js → mindmap-definition-VGOIOE7T-BFpjN9LY.js} +1 -1
- package/dist/dashboard/client/assets/{pieDiagram-ADFJNKIX-CooPKLnX.js → pieDiagram-ADFJNKIX-GBbQtDBQ.js} +1 -1
- package/dist/dashboard/client/assets/{quadrantDiagram-AYHSOK5B-3soPtaSQ.js → quadrantDiagram-AYHSOK5B-Dm0vOhOw.js} +1 -1
- package/dist/dashboard/client/assets/{requirementDiagram-UZGBJVZJ-rE40t0IG.js → requirementDiagram-UZGBJVZJ-BrKONIV8.js} +1 -1
- package/dist/dashboard/client/assets/{sankeyDiagram-TZEHDZUN-CrgDF_jW.js → sankeyDiagram-TZEHDZUN-IOobtmDc.js} +1 -1
- package/dist/dashboard/client/assets/{sequenceDiagram-WL72ISMW-B628IlDW.js → sequenceDiagram-WL72ISMW-Dnb0bOW5.js} +1 -1
- package/dist/dashboard/client/assets/{stateDiagram-FKZM4ZOC-C4yb7S9D.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-5uLN4f_J.js → timeline-definition-IT6M3QCI-tJogDEHB.js} +1 -1
- package/dist/dashboard/client/assets/{treemap-GDKQZRPO-BHXME3bw.js → treemap-GDKQZRPO-DQY6HADq.js} +1 -1
- package/dist/dashboard/client/assets/{xychartDiagram-PRI3JC2R-BYTod6eI.js → xychartDiagram-PRI3JC2R-DfxeQmTO.js} +1 -1
- package/dist/dashboard/client/index.html +2 -2
- package/dist/dashboard/server.js +292 -162
- package/dist/index.js +295 -8
- package/package.json +2 -2
- package/dist/dashboard/client/assets/channel-yW2sWou_.js +0 -1
- package/dist/dashboard/client/assets/classDiagram-2ON5EDUG-1pMX5UXO.js +0 -1
- package/dist/dashboard/client/assets/classDiagram-v2-WZHVMYZB-1pMX5UXO.js +0 -1
- package/dist/dashboard/client/assets/clone-DQwdw3YR.js +0 -1
- package/dist/dashboard/client/assets/index-BzQ3i_QR.js +0 -458
- package/dist/dashboard/client/assets/index-CGGYXSm-.css +0 -1
- package/dist/dashboard/client/assets/stateDiagram-v2-4FDKWEC3-BoFeOfLI.js +0 -1
package/dist/dashboard/server.js
CHANGED
|
@@ -18413,7 +18413,7 @@ var require_view = __commonJS({
|
|
|
18413
18413
|
var dirname11 = path2.dirname;
|
|
18414
18414
|
var basename3 = path2.basename;
|
|
18415
18415
|
var extname = path2.extname;
|
|
18416
|
-
var
|
|
18416
|
+
var join14 = path2.join;
|
|
18417
18417
|
var resolve3 = path2.resolve;
|
|
18418
18418
|
module.exports = View;
|
|
18419
18419
|
function View(name, options) {
|
|
@@ -18461,12 +18461,12 @@ var require_view = __commonJS({
|
|
|
18461
18461
|
};
|
|
18462
18462
|
View.prototype.resolve = function resolve4(dir, file) {
|
|
18463
18463
|
var ext = this.ext;
|
|
18464
|
-
var path3 =
|
|
18464
|
+
var path3 = join14(dir, file);
|
|
18465
18465
|
var stat = tryStat(path3);
|
|
18466
18466
|
if (stat && stat.isFile()) {
|
|
18467
18467
|
return path3;
|
|
18468
18468
|
}
|
|
18469
|
-
path3 =
|
|
18469
|
+
path3 = join14(dir, basename3(file, ext), "index" + ext);
|
|
18470
18470
|
stat = tryStat(path3);
|
|
18471
18471
|
if (stat && stat.isFile()) {
|
|
18472
18472
|
return path3;
|
|
@@ -19099,7 +19099,7 @@ var require_send = __commonJS({
|
|
|
19099
19099
|
var Stream = __require("stream");
|
|
19100
19100
|
var util = __require("util");
|
|
19101
19101
|
var extname = path2.extname;
|
|
19102
|
-
var
|
|
19102
|
+
var join14 = path2.join;
|
|
19103
19103
|
var normalize = path2.normalize;
|
|
19104
19104
|
var resolve3 = path2.resolve;
|
|
19105
19105
|
var sep = path2.sep;
|
|
@@ -19318,7 +19318,7 @@ var require_send = __commonJS({
|
|
|
19318
19318
|
return res;
|
|
19319
19319
|
}
|
|
19320
19320
|
parts = path3.split(sep);
|
|
19321
|
-
path3 = normalize(
|
|
19321
|
+
path3 = normalize(join14(root, path3));
|
|
19322
19322
|
} else {
|
|
19323
19323
|
if (UP_PATH_REGEXP.test(path3)) {
|
|
19324
19324
|
debug('malicious path "%s"', path3);
|
|
@@ -19453,7 +19453,7 @@ var require_send = __commonJS({
|
|
|
19453
19453
|
if (err) return self.onStatError(err);
|
|
19454
19454
|
return self.error(404);
|
|
19455
19455
|
}
|
|
19456
|
-
var p =
|
|
19456
|
+
var p = join14(path3, self._index[i]);
|
|
19457
19457
|
debug('stat "%s"', p);
|
|
19458
19458
|
fs6.stat(p, function(err2, stat) {
|
|
19459
19459
|
if (err2) return next(err2);
|
|
@@ -20592,7 +20592,7 @@ var require_application = __commonJS({
|
|
|
20592
20592
|
"../../node_modules/.pnpm/express@4.22.1/node_modules/express/lib/application.js"(exports, module) {
|
|
20593
20593
|
"use strict";
|
|
20594
20594
|
var finalhandler = require_finalhandler();
|
|
20595
|
-
var
|
|
20595
|
+
var Router12 = require_router();
|
|
20596
20596
|
var methods = require_methods();
|
|
20597
20597
|
var middleware = require_init();
|
|
20598
20598
|
var query = require_query();
|
|
@@ -20657,7 +20657,7 @@ var require_application = __commonJS({
|
|
|
20657
20657
|
};
|
|
20658
20658
|
app2.lazyrouter = function lazyrouter() {
|
|
20659
20659
|
if (!this._router) {
|
|
20660
|
-
this._router = new
|
|
20660
|
+
this._router = new Router12({
|
|
20661
20661
|
caseSensitive: this.enabled("case sensitive routing"),
|
|
20662
20662
|
strict: this.enabled("strict routing")
|
|
20663
20663
|
});
|
|
@@ -22521,7 +22521,7 @@ var require_express = __commonJS({
|
|
|
22521
22521
|
var mixin = require_merge_descriptors();
|
|
22522
22522
|
var proto = require_application();
|
|
22523
22523
|
var Route = require_route();
|
|
22524
|
-
var
|
|
22524
|
+
var Router12 = require_router();
|
|
22525
22525
|
var req = require_request();
|
|
22526
22526
|
var res = require_response();
|
|
22527
22527
|
exports = module.exports = createApplication;
|
|
@@ -22544,7 +22544,7 @@ var require_express = __commonJS({
|
|
|
22544
22544
|
exports.request = req;
|
|
22545
22545
|
exports.response = res;
|
|
22546
22546
|
exports.Route = Route;
|
|
22547
|
-
exports.Router =
|
|
22547
|
+
exports.Router = Router12;
|
|
22548
22548
|
exports.json = bodyParser.json;
|
|
22549
22549
|
exports.query = require_query();
|
|
22550
22550
|
exports.raw = bodyParser.raw;
|
|
@@ -23179,10 +23179,10 @@ var init_open = __esm({
|
|
|
23179
23179
|
});
|
|
23180
23180
|
|
|
23181
23181
|
// src/server/index.ts
|
|
23182
|
-
var
|
|
23182
|
+
var import_express12 = __toESM(require_express2(), 1);
|
|
23183
23183
|
import { createServer } from "node:http";
|
|
23184
|
-
import { existsSync as
|
|
23185
|
-
import { join as
|
|
23184
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3 } from "node:fs";
|
|
23185
|
+
import { join as join13, dirname as dirname10, resolve as resolve2 } from "node:path";
|
|
23186
23186
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
23187
23187
|
import { randomBytes } from "node:crypto";
|
|
23188
23188
|
import { Server as SocketIOServer } from "socket.io";
|
|
@@ -24008,24 +24008,24 @@ function enrichSession(db, session) {
|
|
|
24008
24008
|
let reviewPhaseNumber = 0;
|
|
24009
24009
|
let reviewPhase = "";
|
|
24010
24010
|
if (hasReview) {
|
|
24011
|
+
const derived = deriveReviewPhase(db, session.id);
|
|
24011
24012
|
if (session.workflow_type === "review") {
|
|
24012
|
-
reviewPhaseNumber = session.phase_number;
|
|
24013
|
-
reviewPhase = session.current_phase;
|
|
24013
|
+
reviewPhaseNumber = Math.max(session.phase_number, derived);
|
|
24014
24014
|
} else {
|
|
24015
|
-
reviewPhaseNumber =
|
|
24016
|
-
reviewPhase = REVIEW_PHASE_NAMES[reviewPhaseNumber - 1] ?? "context";
|
|
24015
|
+
reviewPhaseNumber = derived;
|
|
24017
24016
|
}
|
|
24017
|
+
reviewPhase = REVIEW_PHASE_NAMES[reviewPhaseNumber - 1] ?? "context";
|
|
24018
24018
|
}
|
|
24019
24019
|
let mapPhaseNumber = 0;
|
|
24020
24020
|
let mapPhase = "";
|
|
24021
24021
|
if (hasMap) {
|
|
24022
|
+
const derived = deriveMapPhase(db, session.id);
|
|
24022
24023
|
if (session.workflow_type === "map") {
|
|
24023
|
-
mapPhaseNumber = session.phase_number;
|
|
24024
|
-
mapPhase = session.current_phase;
|
|
24024
|
+
mapPhaseNumber = Math.max(session.phase_number, derived);
|
|
24025
24025
|
} else {
|
|
24026
|
-
mapPhaseNumber =
|
|
24027
|
-
mapPhase = MAP_PHASE_NAMES[mapPhaseNumber - 1] ?? "map-context";
|
|
24026
|
+
mapPhaseNumber = derived;
|
|
24028
24027
|
}
|
|
24028
|
+
mapPhase = MAP_PHASE_NAMES[mapPhaseNumber - 1] ?? "map-context";
|
|
24029
24029
|
}
|
|
24030
24030
|
const latestRound = rounds.length > 0 ? rounds[rounds.length - 1] : null;
|
|
24031
24031
|
const latestVerdict = latestRound?.verdict ?? null;
|
|
@@ -25190,11 +25190,37 @@ function resolveLocalCli() {
|
|
|
25190
25190
|
}
|
|
25191
25191
|
|
|
25192
25192
|
// src/server/socket/command-runner.ts
|
|
25193
|
+
function shellSplit(str) {
|
|
25194
|
+
const tokens = [];
|
|
25195
|
+
let current = "";
|
|
25196
|
+
let quote = null;
|
|
25197
|
+
for (let i = 0; i < str.length; i++) {
|
|
25198
|
+
const ch = str[i];
|
|
25199
|
+
if (quote) {
|
|
25200
|
+
if (ch === quote) {
|
|
25201
|
+
quote = null;
|
|
25202
|
+
} else {
|
|
25203
|
+
current += ch;
|
|
25204
|
+
}
|
|
25205
|
+
} else if (ch === '"' || ch === "'") {
|
|
25206
|
+
quote = ch;
|
|
25207
|
+
} else if (/\s/.test(ch)) {
|
|
25208
|
+
if (current) {
|
|
25209
|
+
tokens.push(current);
|
|
25210
|
+
current = "";
|
|
25211
|
+
}
|
|
25212
|
+
} else {
|
|
25213
|
+
current += ch;
|
|
25214
|
+
}
|
|
25215
|
+
}
|
|
25216
|
+
if (current) tokens.push(current);
|
|
25217
|
+
return tokens;
|
|
25218
|
+
}
|
|
25193
25219
|
var ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
25194
25220
|
"progress",
|
|
25195
25221
|
"state"
|
|
25196
25222
|
]);
|
|
25197
|
-
var AI_COMMANDS = /* @__PURE__ */ new Set(["map", "review", "translate-review-to-single-human", "address"]);
|
|
25223
|
+
var AI_COMMANDS = /* @__PURE__ */ new Set(["map", "review", "translate-review-to-single-human", "address", "create-reviewer", "sync-reviewers"]);
|
|
25198
25224
|
var MAX_CONCURRENT = 3;
|
|
25199
25225
|
var activeCommands = /* @__PURE__ */ new Map();
|
|
25200
25226
|
function getActiveCommands() {
|
|
@@ -25216,7 +25242,7 @@ function registerCommandHandlers(io2, socket, db, ocrDir, aiCliService) {
|
|
|
25216
25242
|
}
|
|
25217
25243
|
const { command } = payload;
|
|
25218
25244
|
const normalized = command.replace(/^ocr\s+/, "");
|
|
25219
|
-
const parts = normalized
|
|
25245
|
+
const parts = shellSplit(normalized);
|
|
25220
25246
|
const baseCommand = parts[0] ?? "";
|
|
25221
25247
|
const subArgs = parts.slice(1);
|
|
25222
25248
|
if (!ALLOWED_COMMANDS.has(baseCommand) && !AI_COMMANDS.has(baseCommand)) {
|
|
@@ -25373,34 +25399,68 @@ function spawnAiCommand(io2, _socket, db, ocrDir, executionId, baseCommand, subA
|
|
|
25373
25399
|
finishExecution(io2, db, ocrDir, executionId, 1, content);
|
|
25374
25400
|
return;
|
|
25375
25401
|
}
|
|
25376
|
-
|
|
25377
|
-
|
|
25378
|
-
|
|
25379
|
-
|
|
25380
|
-
|
|
25381
|
-
|
|
25382
|
-
|
|
25383
|
-
|
|
25384
|
-
|
|
25385
|
-
|
|
25386
|
-
|
|
25387
|
-
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
i
|
|
25402
|
+
const promptLines = [];
|
|
25403
|
+
if (baseCommand === "create-reviewer" || baseCommand === "sync-reviewers") {
|
|
25404
|
+
const argsStr = subArgs.length > 0 ? subArgs.join(" ") : "";
|
|
25405
|
+
promptLines.push(
|
|
25406
|
+
`Follow the instructions below to run the OCR ${baseCommand} workflow.`,
|
|
25407
|
+
"",
|
|
25408
|
+
`Arguments: ${argsStr || "none"}`
|
|
25409
|
+
);
|
|
25410
|
+
} else {
|
|
25411
|
+
let target = "staged changes";
|
|
25412
|
+
let requirements = "";
|
|
25413
|
+
let team = "";
|
|
25414
|
+
const reviewerDescriptions = [];
|
|
25415
|
+
const options = [];
|
|
25416
|
+
let i = 0;
|
|
25417
|
+
while (i < subArgs.length) {
|
|
25418
|
+
const arg = subArgs[i] ?? "";
|
|
25419
|
+
if (arg === "--fresh") {
|
|
25420
|
+
options.push("--fresh");
|
|
25421
|
+
i++;
|
|
25422
|
+
} else if (arg === "--requirements" && i + 1 < subArgs.length) {
|
|
25423
|
+
requirements = subArgs.slice(i + 1).join(" ");
|
|
25424
|
+
break;
|
|
25425
|
+
} else if (arg === "--team" && i + 1 < subArgs.length) {
|
|
25426
|
+
team = subArgs[i + 1] ?? "";
|
|
25427
|
+
i += 2;
|
|
25428
|
+
} else if (arg === "--reviewer" && i + 1 < subArgs.length) {
|
|
25429
|
+
const raw = subArgs[i + 1] ?? "";
|
|
25430
|
+
const countMatch = raw.match(/^(\d+):(.+)$/);
|
|
25431
|
+
if (countMatch) {
|
|
25432
|
+
reviewerDescriptions.push({ description: countMatch[2], count: parseInt(countMatch[1], 10) });
|
|
25433
|
+
} else {
|
|
25434
|
+
reviewerDescriptions.push({ description: raw, count: 1 });
|
|
25435
|
+
}
|
|
25436
|
+
i += 2;
|
|
25437
|
+
} else if (!arg.startsWith("--")) {
|
|
25438
|
+
target = arg;
|
|
25439
|
+
i++;
|
|
25440
|
+
} else {
|
|
25441
|
+
i++;
|
|
25442
|
+
}
|
|
25443
|
+
}
|
|
25444
|
+
const optionsStr = options.length > 0 ? options.join(" ") : "none";
|
|
25445
|
+
promptLines.push(
|
|
25446
|
+
`Follow the instructions below to run the OCR ${baseCommand} workflow.`,
|
|
25447
|
+
"",
|
|
25448
|
+
`Target: ${target}`,
|
|
25449
|
+
`Options: ${optionsStr}`
|
|
25450
|
+
);
|
|
25451
|
+
if (team) {
|
|
25452
|
+
promptLines.push(`Team: ${team}`);
|
|
25453
|
+
}
|
|
25454
|
+
for (const { description, count } of reviewerDescriptions) {
|
|
25455
|
+
if (count > 1) {
|
|
25456
|
+
promptLines.push(`Reviewer (x${count}): ${description}`);
|
|
25457
|
+
} else {
|
|
25458
|
+
promptLines.push(`Reviewer: ${description}`);
|
|
25459
|
+
}
|
|
25460
|
+
}
|
|
25461
|
+
if (requirements) {
|
|
25462
|
+
promptLines.push(`Requirements: ${requirements}`);
|
|
25393
25463
|
}
|
|
25394
|
-
}
|
|
25395
|
-
const optionsStr = options.length > 0 ? options.join(" ") : "none";
|
|
25396
|
-
const promptLines = [
|
|
25397
|
-
`Follow the instructions below to run the OCR ${baseCommand} workflow.`,
|
|
25398
|
-
"",
|
|
25399
|
-
`Target: ${target}`,
|
|
25400
|
-
`Options: ${optionsStr}`
|
|
25401
|
-
];
|
|
25402
|
-
if (requirements) {
|
|
25403
|
-
promptLines.push(`Requirements: ${requirements}`);
|
|
25404
25464
|
}
|
|
25405
25465
|
const localCli = resolveLocalCli();
|
|
25406
25466
|
if (localCli) {
|
|
@@ -25768,10 +25828,77 @@ function createChatRouter(db, ocrDir) {
|
|
|
25768
25828
|
return router;
|
|
25769
25829
|
}
|
|
25770
25830
|
|
|
25831
|
+
// src/server/routes/reviewers.ts
|
|
25832
|
+
var import_express11 = __toESM(require_express2(), 1);
|
|
25833
|
+
import { readFileSync as readFileSync5, existsSync as existsSync4, watch } from "node:fs";
|
|
25834
|
+
import { join as join9 } from "node:path";
|
|
25835
|
+
function readReviewersMeta(ocrDir) {
|
|
25836
|
+
const metaPath = join9(ocrDir, "reviewers-meta.json");
|
|
25837
|
+
if (!existsSync4(metaPath)) {
|
|
25838
|
+
return { reviewers: [], defaults: [] };
|
|
25839
|
+
}
|
|
25840
|
+
try {
|
|
25841
|
+
const raw = readFileSync5(metaPath, "utf-8");
|
|
25842
|
+
const meta = JSON.parse(raw);
|
|
25843
|
+
const reviewers = meta.reviewers ?? [];
|
|
25844
|
+
const defaults = reviewers.filter((r) => r.is_default).map((r) => r.id);
|
|
25845
|
+
return { reviewers, defaults };
|
|
25846
|
+
} catch {
|
|
25847
|
+
return { reviewers: [], defaults: [] };
|
|
25848
|
+
}
|
|
25849
|
+
}
|
|
25850
|
+
var VALID_ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
25851
|
+
function createReviewersRouter(ocrDir) {
|
|
25852
|
+
const router = (0, import_express11.Router)();
|
|
25853
|
+
router.get("/", (_req, res) => {
|
|
25854
|
+
res.json(readReviewersMeta(ocrDir));
|
|
25855
|
+
});
|
|
25856
|
+
router.get("/:id/prompt", (req, res) => {
|
|
25857
|
+
const { id } = req.params;
|
|
25858
|
+
if (!id || !VALID_ID_RE.test(id)) {
|
|
25859
|
+
res.status(400).json({ error: "Invalid reviewer ID" });
|
|
25860
|
+
return;
|
|
25861
|
+
}
|
|
25862
|
+
const filePath = join9(ocrDir, "skills", "references", "reviewers", `${id}.md`);
|
|
25863
|
+
if (!existsSync4(filePath)) {
|
|
25864
|
+
res.status(404).json({ error: "Reviewer not found", id });
|
|
25865
|
+
return;
|
|
25866
|
+
}
|
|
25867
|
+
try {
|
|
25868
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
25869
|
+
res.json({ id, content });
|
|
25870
|
+
} catch {
|
|
25871
|
+
res.status(500).json({ error: "Failed to read reviewer file", id });
|
|
25872
|
+
}
|
|
25873
|
+
});
|
|
25874
|
+
return router;
|
|
25875
|
+
}
|
|
25876
|
+
function watchReviewersMeta(ocrDir, io2) {
|
|
25877
|
+
const metaPath = join9(ocrDir, "reviewers-meta.json");
|
|
25878
|
+
let watcher = null;
|
|
25879
|
+
let debounce;
|
|
25880
|
+
try {
|
|
25881
|
+
watcher = watch(metaPath, () => {
|
|
25882
|
+
clearTimeout(debounce);
|
|
25883
|
+
debounce = setTimeout(() => {
|
|
25884
|
+
const data = readReviewersMeta(ocrDir);
|
|
25885
|
+
io2.emit("reviewers:updated", data);
|
|
25886
|
+
}, 200);
|
|
25887
|
+
});
|
|
25888
|
+
watcher.on("error", () => {
|
|
25889
|
+
});
|
|
25890
|
+
} catch {
|
|
25891
|
+
}
|
|
25892
|
+
return () => {
|
|
25893
|
+
clearTimeout(debounce);
|
|
25894
|
+
watcher?.close();
|
|
25895
|
+
};
|
|
25896
|
+
}
|
|
25897
|
+
|
|
25771
25898
|
// src/server/services/filesystem-sync.ts
|
|
25772
|
-
import { readdirSync, readFileSync as
|
|
25773
|
-
import { join as
|
|
25774
|
-
import { watch } from "chokidar";
|
|
25899
|
+
import { readdirSync, readFileSync as readFileSync6, statSync, existsSync as existsSync5 } from "node:fs";
|
|
25900
|
+
import { join as join10, basename as basename2, dirname as dirname7, relative } from "node:path";
|
|
25901
|
+
import { watch as watch2 } from "chokidar";
|
|
25775
25902
|
|
|
25776
25903
|
// src/server/services/parsers/reviewer-parser.ts
|
|
25777
25904
|
var FINDING_HEADING_RE = /^#{2,3}\s+(?:Finding|Issue|Suggestion)\s*(?:\d+)?\s*[:\s]*\s*(.*)/i;
|
|
@@ -25952,67 +26079,67 @@ var FilesystemSync = class {
|
|
|
25952
26079
|
onSync;
|
|
25953
26080
|
// ── 6.1: Full Scan ──
|
|
25954
26081
|
async fullScan() {
|
|
25955
|
-
if (!
|
|
26082
|
+
if (!existsSync5(this.sessionsDir)) return;
|
|
25956
26083
|
const entries = readdirSync(this.sessionsDir, { withFileTypes: true });
|
|
25957
26084
|
for (const entry of entries) {
|
|
25958
26085
|
if (!entry.isDirectory()) continue;
|
|
25959
26086
|
const sessionId = entry.name;
|
|
25960
|
-
const sessionDir =
|
|
26087
|
+
const sessionDir = join10(this.sessionsDir, sessionId);
|
|
25961
26088
|
this.syncSession(sessionId, sessionDir);
|
|
25962
26089
|
}
|
|
25963
26090
|
}
|
|
25964
26091
|
syncSession(sessionId, sessionDir) {
|
|
25965
26092
|
this.ensureSessionRow(sessionId, sessionDir);
|
|
25966
|
-
const roundsDir =
|
|
25967
|
-
if (
|
|
26093
|
+
const roundsDir = join10(sessionDir, "rounds");
|
|
26094
|
+
if (existsSync5(roundsDir)) {
|
|
25968
26095
|
const rounds = readdirSync(roundsDir, { withFileTypes: true });
|
|
25969
26096
|
for (const roundEntry of rounds) {
|
|
25970
26097
|
if (!roundEntry.isDirectory()) continue;
|
|
25971
26098
|
const roundMatch = roundEntry.name.match(/^round-(\d+)$/);
|
|
25972
26099
|
if (!roundMatch) continue;
|
|
25973
26100
|
const roundNumber = parseInt(roundMatch[1] ?? "0", 10);
|
|
25974
|
-
const roundDir =
|
|
25975
|
-
const reviewsDir =
|
|
25976
|
-
if (
|
|
26101
|
+
const roundDir = join10(roundsDir, roundEntry.name);
|
|
26102
|
+
const reviewsDir = join10(roundDir, "reviews");
|
|
26103
|
+
if (existsSync5(reviewsDir)) {
|
|
25977
26104
|
const reviewFiles = readdirSync(reviewsDir).filter((f) => f.endsWith(".md"));
|
|
25978
26105
|
for (const reviewFile of reviewFiles) {
|
|
25979
|
-
const filePath =
|
|
26106
|
+
const filePath = join10(reviewsDir, reviewFile);
|
|
25980
26107
|
this.processReviewerOutput(sessionId, roundNumber, filePath, reviewFile);
|
|
25981
26108
|
}
|
|
25982
26109
|
}
|
|
25983
|
-
const roundMetaPath =
|
|
25984
|
-
if (
|
|
26110
|
+
const roundMetaPath = join10(roundDir, "round-meta.json");
|
|
26111
|
+
if (existsSync5(roundMetaPath)) {
|
|
25985
26112
|
this.processRoundMeta(sessionId, roundNumber, roundMetaPath);
|
|
25986
26113
|
}
|
|
25987
|
-
const finalPath =
|
|
25988
|
-
if (
|
|
26114
|
+
const finalPath = join10(roundDir, "final.md");
|
|
26115
|
+
if (existsSync5(finalPath)) {
|
|
25989
26116
|
this.processFinalMd(sessionId, roundNumber, finalPath);
|
|
25990
26117
|
}
|
|
25991
|
-
const finalHumanPath =
|
|
25992
|
-
if (
|
|
26118
|
+
const finalHumanPath = join10(roundDir, "final-human.md");
|
|
26119
|
+
if (existsSync5(finalHumanPath)) {
|
|
25993
26120
|
this.processGenericArtifact(sessionId, "final-human", finalHumanPath, roundNumber);
|
|
25994
26121
|
}
|
|
25995
|
-
const discoursePath =
|
|
25996
|
-
if (
|
|
26122
|
+
const discoursePath = join10(roundDir, "discourse.md");
|
|
26123
|
+
if (existsSync5(discoursePath)) {
|
|
25997
26124
|
this.processGenericArtifact(sessionId, "discourse", discoursePath, roundNumber);
|
|
25998
26125
|
}
|
|
25999
26126
|
}
|
|
26000
26127
|
}
|
|
26001
|
-
const mapDir =
|
|
26002
|
-
if (
|
|
26128
|
+
const mapDir = join10(sessionDir, "map", "runs");
|
|
26129
|
+
if (existsSync5(mapDir)) {
|
|
26003
26130
|
const runs = readdirSync(mapDir, { withFileTypes: true });
|
|
26004
26131
|
for (const runEntry of runs) {
|
|
26005
26132
|
if (!runEntry.isDirectory()) continue;
|
|
26006
26133
|
const runMatch = runEntry.name.match(/^run-(\d+)$/);
|
|
26007
26134
|
if (!runMatch) continue;
|
|
26008
26135
|
const runNumber = parseInt(runMatch[1] ?? "0", 10);
|
|
26009
|
-
const runDir =
|
|
26010
|
-
const mapMetaPath =
|
|
26011
|
-
if (
|
|
26136
|
+
const runDir = join10(mapDir, runEntry.name);
|
|
26137
|
+
const mapMetaPath = join10(runDir, "map-meta.json");
|
|
26138
|
+
if (existsSync5(mapMetaPath)) {
|
|
26012
26139
|
this.processMapMeta(sessionId, runNumber, mapMetaPath);
|
|
26013
26140
|
}
|
|
26014
|
-
const mapPath =
|
|
26015
|
-
if (
|
|
26141
|
+
const mapPath = join10(runDir, "map.md");
|
|
26142
|
+
if (existsSync5(mapPath)) {
|
|
26016
26143
|
this.processMapMd(sessionId, runNumber, mapPath);
|
|
26017
26144
|
}
|
|
26018
26145
|
const mapArtifacts = [
|
|
@@ -26021,8 +26148,8 @@ var FilesystemSync = class {
|
|
|
26021
26148
|
["requirements-mapping.md", "requirements-mapping"]
|
|
26022
26149
|
];
|
|
26023
26150
|
for (const [fileName, artifactType] of mapArtifacts) {
|
|
26024
|
-
const filePath =
|
|
26025
|
-
if (
|
|
26151
|
+
const filePath = join10(runDir, fileName);
|
|
26152
|
+
if (existsSync5(filePath)) {
|
|
26026
26153
|
this.processGenericArtifact(sessionId, artifactType, filePath, void 0, runNumber);
|
|
26027
26154
|
}
|
|
26028
26155
|
}
|
|
@@ -26033,8 +26160,8 @@ var FilesystemSync = class {
|
|
|
26033
26160
|
["discovered-standards.md", "discovered-standards"]
|
|
26034
26161
|
];
|
|
26035
26162
|
for (const [fileName, artifactType] of sessionArtifacts) {
|
|
26036
|
-
const filePath =
|
|
26037
|
-
if (
|
|
26163
|
+
const filePath = join10(sessionDir, fileName);
|
|
26164
|
+
if (existsSync5(filePath)) {
|
|
26038
26165
|
this.processGenericArtifact(sessionId, artifactType, filePath);
|
|
26039
26166
|
}
|
|
26040
26167
|
}
|
|
@@ -26043,17 +26170,17 @@ var FilesystemSync = class {
|
|
|
26043
26170
|
ensureSessionRow(sessionId, sessionDir) {
|
|
26044
26171
|
const branchMatch = sessionId.match(/^\d{4}-\d{2}-\d{2}-(.+)$/);
|
|
26045
26172
|
const branch = branchMatch?.[1] ?? "unknown";
|
|
26046
|
-
const hasRoundsDir =
|
|
26047
|
-
const hasMapDir =
|
|
26173
|
+
const hasRoundsDir = existsSync5(join10(sessionDir, "rounds"));
|
|
26174
|
+
const hasMapDir = existsSync5(join10(sessionDir, "map"));
|
|
26048
26175
|
const workflowType = hasMapDir && !hasRoundsDir ? "map" : "review";
|
|
26049
26176
|
let currentRound = 1;
|
|
26050
26177
|
if (hasRoundsDir) {
|
|
26051
|
-
const roundDirs = readdirSync(
|
|
26178
|
+
const roundDirs = readdirSync(join10(sessionDir, "rounds")).filter((d) => d.match(/^round-\d+$/));
|
|
26052
26179
|
currentRound = Math.max(1, roundDirs.length);
|
|
26053
26180
|
}
|
|
26054
26181
|
let currentMapRun = 1;
|
|
26055
|
-
const mapRunsDir =
|
|
26056
|
-
if (
|
|
26182
|
+
const mapRunsDir = join10(sessionDir, "map", "runs");
|
|
26183
|
+
if (existsSync5(mapRunsDir)) {
|
|
26057
26184
|
const runDirs = readdirSync(mapRunsDir).filter((d) => d.match(/^run-\d+$/));
|
|
26058
26185
|
currentMapRun = Math.max(1, runDirs.length);
|
|
26059
26186
|
}
|
|
@@ -26061,40 +26188,40 @@ var FilesystemSync = class {
|
|
|
26061
26188
|
let phaseNumber = 1;
|
|
26062
26189
|
let status = "active";
|
|
26063
26190
|
if (workflowType === "review" && hasRoundsDir) {
|
|
26064
|
-
const roundDir =
|
|
26065
|
-
if (
|
|
26191
|
+
const roundDir = join10(sessionDir, "rounds", `round-${currentRound}`);
|
|
26192
|
+
if (existsSync5(join10(roundDir, "final.md"))) {
|
|
26066
26193
|
phase = "complete";
|
|
26067
26194
|
phaseNumber = 8;
|
|
26068
26195
|
status = "closed";
|
|
26069
|
-
} else if (
|
|
26196
|
+
} else if (existsSync5(join10(roundDir, "discourse.md"))) {
|
|
26070
26197
|
phase = "synthesis";
|
|
26071
26198
|
phaseNumber = 7;
|
|
26072
|
-
} else if (
|
|
26199
|
+
} else if (existsSync5(join10(roundDir, "reviews")) && readdirSync(join10(roundDir, "reviews")).filter((f) => f.endsWith(".md")).length > 0) {
|
|
26073
26200
|
phase = "reviews";
|
|
26074
26201
|
phaseNumber = 4;
|
|
26075
|
-
} else if (
|
|
26202
|
+
} else if (existsSync5(join10(sessionDir, "context.md"))) {
|
|
26076
26203
|
phase = "analysis";
|
|
26077
26204
|
phaseNumber = 3;
|
|
26078
|
-
} else if (
|
|
26205
|
+
} else if (existsSync5(join10(sessionDir, "discovered-standards.md"))) {
|
|
26079
26206
|
phase = "change-context";
|
|
26080
26207
|
phaseNumber = 2;
|
|
26081
26208
|
}
|
|
26082
26209
|
} else if (workflowType === "map" && hasMapDir) {
|
|
26083
|
-
const runDir =
|
|
26084
|
-
if (
|
|
26210
|
+
const runDir = join10(mapRunsDir, `run-${currentMapRun}`);
|
|
26211
|
+
if (existsSync5(join10(runDir, "map.md"))) {
|
|
26085
26212
|
phase = "complete";
|
|
26086
26213
|
phaseNumber = 6;
|
|
26087
26214
|
status = "closed";
|
|
26088
|
-
} else if (
|
|
26215
|
+
} else if (existsSync5(join10(runDir, "requirements-mapping.md"))) {
|
|
26089
26216
|
phase = "synthesis";
|
|
26090
26217
|
phaseNumber = 5;
|
|
26091
|
-
} else if (
|
|
26218
|
+
} else if (existsSync5(join10(runDir, "flow-analysis.md"))) {
|
|
26092
26219
|
phase = "requirements-mapping";
|
|
26093
26220
|
phaseNumber = 4;
|
|
26094
|
-
} else if (
|
|
26221
|
+
} else if (existsSync5(join10(runDir, "topology.md"))) {
|
|
26095
26222
|
phase = "flow-analysis";
|
|
26096
26223
|
phaseNumber = 3;
|
|
26097
|
-
} else if (
|
|
26224
|
+
} else if (existsSync5(join10(sessionDir, "discovered-standards.md"))) {
|
|
26098
26225
|
phase = "topology";
|
|
26099
26226
|
phaseNumber = 2;
|
|
26100
26227
|
}
|
|
@@ -26157,12 +26284,12 @@ var FilesystemSync = class {
|
|
|
26157
26284
|
);
|
|
26158
26285
|
if (existingRun && this.shouldSkip(filePath, existingRun["parsed_at"] ?? null)) return;
|
|
26159
26286
|
if (existingRun?.["source"] === "orchestrator") {
|
|
26160
|
-
const content2 =
|
|
26287
|
+
const content2 = readFileSync6(filePath, "utf-8");
|
|
26161
26288
|
const action2 = this.upsertMarkdownArtifact(sessionId, "map", filePath, content2, void 0);
|
|
26162
26289
|
this.emitArtifactEvent(action2, { sessionId, artifactType: "map", filePath });
|
|
26163
26290
|
return;
|
|
26164
26291
|
}
|
|
26165
|
-
const content =
|
|
26292
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
26166
26293
|
const parsed = parseMapMd(content);
|
|
26167
26294
|
this.db.run(
|
|
26168
26295
|
`INSERT OR REPLACE INTO map_runs (session_id, run_number, file_count, map_md_path, parsed_at, source)
|
|
@@ -26244,10 +26371,10 @@ var FilesystemSync = class {
|
|
|
26244
26371
|
}
|
|
26245
26372
|
const session = queryFirst(
|
|
26246
26373
|
this.db,
|
|
26247
|
-
"SELECT current_phase, workflow_type FROM sessions WHERE id = ?",
|
|
26374
|
+
"SELECT current_phase, phase_number, workflow_type FROM sessions WHERE id = ?",
|
|
26248
26375
|
[sessionId]
|
|
26249
26376
|
);
|
|
26250
|
-
if (session && session["workflow_type"] === "map" && session["current_phase"] !== "complete") {
|
|
26377
|
+
if (session && session["workflow_type"] === "map" && (session["current_phase"] !== "complete" || session["phase_number"] < 6)) {
|
|
26251
26378
|
this.db.run(
|
|
26252
26379
|
`UPDATE sessions SET current_phase = 'complete', phase_number = 6, status = 'closed', updated_at = datetime('now')
|
|
26253
26380
|
WHERE id = ?`,
|
|
@@ -26282,7 +26409,7 @@ var FilesystemSync = class {
|
|
|
26282
26409
|
const roundId = roundRow?.["id"];
|
|
26283
26410
|
if (!roundId) return;
|
|
26284
26411
|
if (roundRow?.["source"] === "orchestrator") {
|
|
26285
|
-
const content2 =
|
|
26412
|
+
const content2 = readFileSync6(filePath, "utf-8");
|
|
26286
26413
|
const action2 = this.upsertMarkdownArtifact(sessionId, "reviewer-output", filePath, content2, roundNumber);
|
|
26287
26414
|
this.emitArtifactEvent(action2, {
|
|
26288
26415
|
sessionId,
|
|
@@ -26301,7 +26428,7 @@ var FilesystemSync = class {
|
|
|
26301
26428
|
[roundId, reviewerType, instanceNumber]
|
|
26302
26429
|
);
|
|
26303
26430
|
if (existingOutput && this.shouldSkip(filePath, existingOutput["parsed_at"] ?? null)) return;
|
|
26304
|
-
const content =
|
|
26431
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
26305
26432
|
const parsed = parseReviewerOutput(content);
|
|
26306
26433
|
this.db.run(
|
|
26307
26434
|
`INSERT OR REPLACE INTO reviewer_outputs (round_id, reviewer_type, instance_number, file_path, finding_count, parsed_at)
|
|
@@ -26389,7 +26516,7 @@ var FilesystemSync = class {
|
|
|
26389
26516
|
if (existingRound?.["source"] === "orchestrator" && this.shouldSkip(filePath, existingRound["parsed_at"] ?? null)) return;
|
|
26390
26517
|
let raw;
|
|
26391
26518
|
try {
|
|
26392
|
-
raw = JSON.parse(
|
|
26519
|
+
raw = JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
26393
26520
|
} catch {
|
|
26394
26521
|
console.error(`[FilesystemSync] Failed to parse ${filePath}`);
|
|
26395
26522
|
return;
|
|
@@ -26439,7 +26566,7 @@ var FilesystemSync = class {
|
|
|
26439
26566
|
const reviewerType = reviewer.type ?? "unknown";
|
|
26440
26567
|
const instanceNumber = reviewer.instance ?? 1;
|
|
26441
26568
|
const findings = reviewer.findings ?? [];
|
|
26442
|
-
const reviewerMdPath =
|
|
26569
|
+
const reviewerMdPath = join10(roundDir, "reviews", `${reviewerType}-${instanceNumber}.md`);
|
|
26443
26570
|
this.db.run(
|
|
26444
26571
|
`INSERT OR REPLACE INTO reviewer_outputs (round_id, reviewer_type, instance_number, file_path, finding_count, parsed_at)
|
|
26445
26572
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
@@ -26537,7 +26664,7 @@ var FilesystemSync = class {
|
|
|
26537
26664
|
if (existingRun?.["source"] === "orchestrator" && this.shouldSkip(filePath, existingRun["parsed_at"] ?? null)) return;
|
|
26538
26665
|
let raw;
|
|
26539
26666
|
try {
|
|
26540
|
-
raw = JSON.parse(
|
|
26667
|
+
raw = JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
26541
26668
|
} catch {
|
|
26542
26669
|
console.error(`[FilesystemSync] Failed to parse ${filePath}`);
|
|
26543
26670
|
return;
|
|
@@ -26665,7 +26792,7 @@ var FilesystemSync = class {
|
|
|
26665
26792
|
);
|
|
26666
26793
|
const isOrchestratorSource = existingRound?.["source"] === "orchestrator";
|
|
26667
26794
|
if (!isOrchestratorSource && existingRound && this.shouldSkip(filePath, existingRound["parsed_at"] ?? null)) return;
|
|
26668
|
-
const content =
|
|
26795
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
26669
26796
|
if (isOrchestratorSource) {
|
|
26670
26797
|
this.db.run(
|
|
26671
26798
|
`UPDATE review_rounds SET final_md_path = ?, parsed_at = ?
|
|
@@ -26708,7 +26835,7 @@ var FilesystemSync = class {
|
|
|
26708
26835
|
"SELECT current_phase, phase_number, status FROM sessions WHERE id = ?",
|
|
26709
26836
|
[sessionId]
|
|
26710
26837
|
);
|
|
26711
|
-
if (session && session["current_phase"] !== "complete") {
|
|
26838
|
+
if (session && (session["current_phase"] !== "complete" || session["phase_number"] < 8)) {
|
|
26712
26839
|
this.db.run(
|
|
26713
26840
|
`UPDATE sessions SET current_phase = 'complete', phase_number = 8, status = 'closed', updated_at = datetime('now')
|
|
26714
26841
|
WHERE id = ?`,
|
|
@@ -26738,7 +26865,7 @@ var FilesystemSync = class {
|
|
|
26738
26865
|
[sessionId, artifactType, relPath]
|
|
26739
26866
|
);
|
|
26740
26867
|
if (existing && this.shouldSkip(filePath, existing["parsed_at"] ?? null)) return;
|
|
26741
|
-
const content =
|
|
26868
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
26742
26869
|
const action = this.upsertMarkdownArtifact(sessionId, artifactType, filePath, content, roundNumber);
|
|
26743
26870
|
this.emitArtifactEvent(action, {
|
|
26744
26871
|
sessionId,
|
|
@@ -26750,7 +26877,7 @@ var FilesystemSync = class {
|
|
|
26750
26877
|
// ── 6.6: Chokidar Watcher ──
|
|
26751
26878
|
startWatching() {
|
|
26752
26879
|
if (this.watcher) return;
|
|
26753
|
-
this.watcher =
|
|
26880
|
+
this.watcher = watch2(this.sessionsDir, {
|
|
26754
26881
|
persistent: true,
|
|
26755
26882
|
ignoreInitial: true,
|
|
26756
26883
|
depth: 10,
|
|
@@ -26797,7 +26924,7 @@ var FilesystemSync = class {
|
|
|
26797
26924
|
const parts = relFromSessions.split("/");
|
|
26798
26925
|
const sessionId = parts[0];
|
|
26799
26926
|
if (!sessionId) return;
|
|
26800
|
-
const sessionDir =
|
|
26927
|
+
const sessionDir = join10(this.sessionsDir, sessionId);
|
|
26801
26928
|
this.ensureSessionRow(sessionId, sessionDir);
|
|
26802
26929
|
const fileName = basename2(filePath);
|
|
26803
26930
|
const reviewerMatch = relFromSessions.match(/rounds\/round-(\d+)\/reviews\/(.+\.md)$/);
|
|
@@ -26869,8 +26996,8 @@ var FilesystemSync = class {
|
|
|
26869
26996
|
};
|
|
26870
26997
|
|
|
26871
26998
|
// src/server/services/db-sync-watcher.ts
|
|
26872
|
-
import { existsSync as
|
|
26873
|
-
import { watch as
|
|
26999
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
|
|
27000
|
+
import { watch as watch3 } from "chokidar";
|
|
26874
27001
|
import initSqlJs3 from "sql.js";
|
|
26875
27002
|
function col(row, key) {
|
|
26876
27003
|
return row[key] ?? null;
|
|
@@ -26891,7 +27018,7 @@ var DbSyncWatcher = class {
|
|
|
26891
27018
|
* Initialize the WASM runtime (called once at startup).
|
|
26892
27019
|
*/
|
|
26893
27020
|
async init() {
|
|
26894
|
-
const wasmBuffer =
|
|
27021
|
+
const wasmBuffer = readFileSync7(locateWasm());
|
|
26895
27022
|
this.wasmBinary = wasmBuffer.buffer.slice(
|
|
26896
27023
|
wasmBuffer.byteOffset,
|
|
26897
27024
|
wasmBuffer.byteOffset + wasmBuffer.byteLength
|
|
@@ -26902,12 +27029,12 @@ var DbSyncWatcher = class {
|
|
|
26902
27029
|
* Start watching the DB file for external changes.
|
|
26903
27030
|
*/
|
|
26904
27031
|
startWatching() {
|
|
26905
|
-
if (!
|
|
27032
|
+
if (!existsSync6(this.dbFilePath)) return;
|
|
26906
27033
|
try {
|
|
26907
27034
|
this.lastMtime = statSync2(this.dbFilePath).mtimeMs;
|
|
26908
27035
|
} catch {
|
|
26909
27036
|
}
|
|
26910
|
-
this.watcher =
|
|
27037
|
+
this.watcher = watch3(this.dbFilePath, {
|
|
26911
27038
|
// Also watch WAL/SHM files that SQLite may create
|
|
26912
27039
|
persistent: true,
|
|
26913
27040
|
ignoreInitial: true,
|
|
@@ -26953,7 +27080,7 @@ var DbSyncWatcher = class {
|
|
|
26953
27080
|
* to avoid overwriting CLI changes.
|
|
26954
27081
|
*/
|
|
26955
27082
|
syncFromDisk() {
|
|
26956
|
-
if (!this.SQL || !
|
|
27083
|
+
if (!this.SQL || !existsSync6(this.dbFilePath)) return;
|
|
26957
27084
|
let currentMtime;
|
|
26958
27085
|
try {
|
|
26959
27086
|
currentMtime = statSync2(this.dbFilePath).mtimeMs;
|
|
@@ -26964,7 +27091,7 @@ var DbSyncWatcher = class {
|
|
|
26964
27091
|
this.lastMtime = currentMtime;
|
|
26965
27092
|
let diskDb = null;
|
|
26966
27093
|
try {
|
|
26967
|
-
const fileBuffer =
|
|
27094
|
+
const fileBuffer = readFileSync7(this.dbFilePath);
|
|
26968
27095
|
diskDb = new this.SQL.Database(fileBuffer);
|
|
26969
27096
|
this.syncSessions(diskDb);
|
|
26970
27097
|
this.syncEvents(diskDb);
|
|
@@ -27221,17 +27348,17 @@ var DbSyncWatcher = class {
|
|
|
27221
27348
|
import { dirname as dirname8 } from "node:path";
|
|
27222
27349
|
|
|
27223
27350
|
// src/server/services/chat-context.ts
|
|
27224
|
-
import { readFileSync as
|
|
27225
|
-
import { join as
|
|
27351
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync2, existsSync as existsSync7 } from "node:fs";
|
|
27352
|
+
import { join as join11 } from "node:path";
|
|
27226
27353
|
function buildChatContext(ocrDir, target) {
|
|
27227
|
-
const sessionsDir =
|
|
27354
|
+
const sessionsDir = join11(ocrDir, "sessions");
|
|
27228
27355
|
if (target.type === "map_run") {
|
|
27229
27356
|
return buildMapRunContext(sessionsDir, target.sessionId, target.runNumber);
|
|
27230
27357
|
}
|
|
27231
27358
|
return buildReviewRoundContext(sessionsDir, target.sessionId, target.roundNumber);
|
|
27232
27359
|
}
|
|
27233
27360
|
function buildMapRunContext(sessionsDir, sessionId, runNumber) {
|
|
27234
|
-
const mapPath =
|
|
27361
|
+
const mapPath = join11(
|
|
27235
27362
|
sessionsDir,
|
|
27236
27363
|
sessionId,
|
|
27237
27364
|
"map",
|
|
@@ -27245,8 +27372,8 @@ function buildMapRunContext(sessionsDir, sessionId, runNumber) {
|
|
|
27245
27372
|
"",
|
|
27246
27373
|
`Below is the Code Review Map that organizes the changeset into reviewable sections:`
|
|
27247
27374
|
];
|
|
27248
|
-
if (
|
|
27249
|
-
const content =
|
|
27375
|
+
if (existsSync7(mapPath)) {
|
|
27376
|
+
const content = readFileSync8(mapPath, "utf-8");
|
|
27250
27377
|
parts.push("");
|
|
27251
27378
|
parts.push("<map>");
|
|
27252
27379
|
parts.push(content);
|
|
@@ -27258,26 +27385,26 @@ function buildMapRunContext(sessionsDir, sessionId, runNumber) {
|
|
|
27258
27385
|
return parts.join("\n");
|
|
27259
27386
|
}
|
|
27260
27387
|
function buildReviewRoundContext(sessionsDir, sessionId, roundNumber) {
|
|
27261
|
-
const roundDir =
|
|
27262
|
-
const finalPath =
|
|
27263
|
-
const reviewersDir =
|
|
27388
|
+
const roundDir = join11(sessionsDir, sessionId, "rounds", `round-${roundNumber}`);
|
|
27389
|
+
const finalPath = join11(roundDir, "final.md");
|
|
27390
|
+
const reviewersDir = join11(roundDir, "reviews");
|
|
27264
27391
|
const parts = [
|
|
27265
27392
|
`You are an expert code reviewer assisting with a code review session.`,
|
|
27266
27393
|
`You are looking at review round #${roundNumber} for session "${sessionId}".`,
|
|
27267
27394
|
"",
|
|
27268
27395
|
`Below are the review artifacts for this round:`
|
|
27269
27396
|
];
|
|
27270
|
-
if (
|
|
27271
|
-
const content =
|
|
27397
|
+
if (existsSync7(finalPath)) {
|
|
27398
|
+
const content = readFileSync8(finalPath, "utf-8");
|
|
27272
27399
|
parts.push("");
|
|
27273
27400
|
parts.push("<final-synthesis>");
|
|
27274
27401
|
parts.push(content);
|
|
27275
27402
|
parts.push("</final-synthesis>");
|
|
27276
27403
|
}
|
|
27277
|
-
if (
|
|
27404
|
+
if (existsSync7(reviewersDir)) {
|
|
27278
27405
|
const files = readdirSync2(reviewersDir).filter((f) => f.endsWith(".md")).sort();
|
|
27279
27406
|
for (const file of files) {
|
|
27280
|
-
const content =
|
|
27407
|
+
const content = readFileSync8(join11(reviewersDir, file), "utf-8");
|
|
27281
27408
|
const reviewerName = file.replace(/\.md$/, "");
|
|
27282
27409
|
parts.push("");
|
|
27283
27410
|
parts.push(`<reviewer name="${reviewerName}">`);
|
|
@@ -27285,7 +27412,7 @@ function buildReviewRoundContext(sessionsDir, sessionId, roundNumber) {
|
|
|
27285
27412
|
parts.push("</reviewer>");
|
|
27286
27413
|
}
|
|
27287
27414
|
}
|
|
27288
|
-
if (!
|
|
27415
|
+
if (!existsSync7(finalPath) && !existsSync7(reviewersDir)) {
|
|
27289
27416
|
parts.push("");
|
|
27290
27417
|
parts.push("(No review artifacts found on disk for this round.)");
|
|
27291
27418
|
}
|
|
@@ -27590,15 +27717,15 @@ function cleanupAllChats() {
|
|
|
27590
27717
|
|
|
27591
27718
|
// src/server/socket/post-handler.ts
|
|
27592
27719
|
import { execFile } from "node:child_process";
|
|
27593
|
-
import { existsSync as
|
|
27720
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync9, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
27594
27721
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
27595
|
-
import { join as
|
|
27722
|
+
import { join as join12, dirname as dirname9, isAbsolute } from "node:path";
|
|
27596
27723
|
import { randomUUID } from "node:crypto";
|
|
27597
27724
|
import { promisify } from "node:util";
|
|
27598
27725
|
var execFileAsync = promisify(execFile);
|
|
27599
27726
|
function resolveSessionDir(sessionDir, ocrDir) {
|
|
27600
27727
|
if (isAbsolute(sessionDir)) return sessionDir;
|
|
27601
|
-
return
|
|
27728
|
+
return join12(dirname9(ocrDir), sessionDir);
|
|
27602
27729
|
}
|
|
27603
27730
|
var BRANCH_PREFIXES = [
|
|
27604
27731
|
"feat",
|
|
@@ -27768,19 +27895,19 @@ function registerPostHandlers(io2, socket, db, ocrDir, aiCliService) {
|
|
|
27768
27895
|
socket.emit("post:error", { error: "Session not found" });
|
|
27769
27896
|
return;
|
|
27770
27897
|
}
|
|
27771
|
-
const sessionDir = session.session_dir ? resolveSessionDir(session.session_dir, ocrDir) :
|
|
27772
|
-
const roundDir =
|
|
27773
|
-
const finalPath =
|
|
27774
|
-
if (!
|
|
27898
|
+
const sessionDir = session.session_dir ? resolveSessionDir(session.session_dir, ocrDir) : join12(ocrDir, "sessions", sessionId);
|
|
27899
|
+
const roundDir = join12(sessionDir, "rounds", `round-${roundNumber}`);
|
|
27900
|
+
const finalPath = join12(roundDir, "final.md");
|
|
27901
|
+
if (!existsSync8(finalPath)) {
|
|
27775
27902
|
socket.emit("post:error", { error: "final.md not found for this round" });
|
|
27776
27903
|
return;
|
|
27777
27904
|
}
|
|
27778
|
-
const humanReviewPath =
|
|
27905
|
+
const humanReviewPath = join12(roundDir, "final-human.md");
|
|
27779
27906
|
const repoRoot = dirname9(ocrDir);
|
|
27780
|
-
const commandMdPath =
|
|
27907
|
+
const commandMdPath = join12(ocrDir, "commands", "translate-review-to-single-human.md");
|
|
27781
27908
|
let commandContent;
|
|
27782
27909
|
try {
|
|
27783
|
-
commandContent =
|
|
27910
|
+
commandContent = readFileSync9(commandMdPath, "utf-8");
|
|
27784
27911
|
} catch {
|
|
27785
27912
|
socket.emit("post:error", {
|
|
27786
27913
|
error: `Command file not found: ${commandMdPath}. Run \`ocr init\` to set up.`
|
|
@@ -27856,9 +27983,9 @@ function registerPostHandlers(io2, socket, db, ocrDir, aiCliService) {
|
|
|
27856
27983
|
}
|
|
27857
27984
|
}
|
|
27858
27985
|
let generatedContent = "";
|
|
27859
|
-
if (
|
|
27986
|
+
if (existsSync8(humanReviewPath)) {
|
|
27860
27987
|
try {
|
|
27861
|
-
generatedContent =
|
|
27988
|
+
generatedContent = readFileSync9(humanReviewPath, "utf-8").trim();
|
|
27862
27989
|
} catch {
|
|
27863
27990
|
}
|
|
27864
27991
|
}
|
|
@@ -27933,10 +28060,10 @@ function registerPostHandlers(io2, socket, db, ocrDir, aiCliService) {
|
|
|
27933
28060
|
socket.emit("post:save-result", { success: false, error: "Session not found" });
|
|
27934
28061
|
return;
|
|
27935
28062
|
}
|
|
27936
|
-
const sessionDir = session.session_dir ? resolveSessionDir(session.session_dir, ocrDir) :
|
|
27937
|
-
const roundDir =
|
|
28063
|
+
const sessionDir = session.session_dir ? resolveSessionDir(session.session_dir, ocrDir) : join12(ocrDir, "sessions", sessionId);
|
|
28064
|
+
const roundDir = join12(sessionDir, "rounds", `round-${roundNumber}`);
|
|
27938
28065
|
mkdirSync2(roundDir, { recursive: true });
|
|
27939
|
-
const filePath =
|
|
28066
|
+
const filePath = join12(roundDir, "final-human.md");
|
|
27940
28067
|
writeFileSync3(filePath, content, { mode: 420 });
|
|
27941
28068
|
saveDb(db, ocrDir);
|
|
27942
28069
|
socket.emit("post:save-result", { success: true });
|
|
@@ -27964,12 +28091,12 @@ function registerPostHandlers(io2, socket, db, ocrDir, aiCliService) {
|
|
|
27964
28091
|
);
|
|
27965
28092
|
tracker.appendOutput(`\u25B8 Posting review to PR #${prNumber}...
|
|
27966
28093
|
`);
|
|
27967
|
-
const tmpDir =
|
|
28094
|
+
const tmpDir = join12(tmpdir2(), "ocr-post-comments");
|
|
27968
28095
|
try {
|
|
27969
28096
|
mkdirSync2(tmpDir, { recursive: true, mode: 448 });
|
|
27970
28097
|
} catch {
|
|
27971
28098
|
}
|
|
27972
|
-
const tmpFile =
|
|
28099
|
+
const tmpFile = join12(tmpDir, `${randomUUID()}.md`);
|
|
27973
28100
|
writeFileSync3(tmpFile, content, { mode: 384 });
|
|
27974
28101
|
const repoRoot = dirname9(ocrDir);
|
|
27975
28102
|
try {
|
|
@@ -28017,7 +28144,7 @@ function cleanupAllPostGenerations() {
|
|
|
28017
28144
|
// src/server/index.ts
|
|
28018
28145
|
var __dirname3 = dirname10(fileURLToPath3(import.meta.url));
|
|
28019
28146
|
var AUTH_TOKEN = randomBytes(32).toString("hex");
|
|
28020
|
-
var app = (0,
|
|
28147
|
+
var app = (0, import_express12.default)();
|
|
28021
28148
|
var httpServer = createServer(app);
|
|
28022
28149
|
var io = new SocketIOServer(httpServer, {
|
|
28023
28150
|
cors: {
|
|
@@ -28026,7 +28153,7 @@ var io = new SocketIOServer(httpServer, {
|
|
|
28026
28153
|
maxHttpBufferSize: 1e6
|
|
28027
28154
|
// 1 MB — explicit default; review if large payloads are needed
|
|
28028
28155
|
});
|
|
28029
|
-
app.use(
|
|
28156
|
+
app.use(import_express12.default.json());
|
|
28030
28157
|
if (process.env.NODE_ENV !== "production") {
|
|
28031
28158
|
app.use((_req, res, next) => {
|
|
28032
28159
|
const origin = _req.headers.origin;
|
|
@@ -28078,12 +28205,12 @@ async function startServer(options = {}) {
|
|
|
28078
28205
|
const ocrDir = resolveOcrDir();
|
|
28079
28206
|
const aiCliService = new AiCliService(ocrDir);
|
|
28080
28207
|
const db = await openDb(ocrDir);
|
|
28081
|
-
const dataDir =
|
|
28082
|
-
const pidFilePath =
|
|
28208
|
+
const dataDir = join13(ocrDir, "data");
|
|
28209
|
+
const pidFilePath = join13(dataDir, "dashboard.pid");
|
|
28083
28210
|
mkdirSync3(dataDir, { recursive: true });
|
|
28084
|
-
if (
|
|
28211
|
+
if (existsSync9(pidFilePath)) {
|
|
28085
28212
|
try {
|
|
28086
|
-
const oldPid = parseInt(
|
|
28213
|
+
const oldPid = parseInt(readFileSync10(pidFilePath, "utf-8").trim(), 10);
|
|
28087
28214
|
if (!isNaN(oldPid)) {
|
|
28088
28215
|
try {
|
|
28089
28216
|
process.kill(oldPid, 0);
|
|
@@ -28183,11 +28310,12 @@ async function startServer(options = {}) {
|
|
|
28183
28310
|
app.use("/api/commands", createCommandsRouter(db));
|
|
28184
28311
|
app.use("/api/config", createConfigRouter(ocrDir, aiCliService));
|
|
28185
28312
|
app.use("/api/sessions", createChatRouter(db, ocrDir));
|
|
28186
|
-
|
|
28187
|
-
|
|
28188
|
-
|
|
28189
|
-
|
|
28190
|
-
const
|
|
28313
|
+
app.use("/api/reviewers", createReviewersRouter(ocrDir));
|
|
28314
|
+
const clientDir = join13(__dirname3, "client");
|
|
28315
|
+
if (process.env.NODE_ENV === "production" && existsSync9(clientDir)) {
|
|
28316
|
+
app.use(import_express12.default.static(clientDir, { index: false }));
|
|
28317
|
+
const indexHtmlPath = join13(clientDir, "index.html");
|
|
28318
|
+
const rawIndexHtml = existsSync9(indexHtmlPath) ? readFileSync10(indexHtmlPath, "utf-8") : "";
|
|
28191
28319
|
const tokenScript = `<script>window.__OCR_TOKEN__=${JSON.stringify(AUTH_TOKEN)};</script>`;
|
|
28192
28320
|
const injectedIndexHtml = rawIndexHtml.replace(
|
|
28193
28321
|
"</head>",
|
|
@@ -28210,7 +28338,7 @@ async function startServer(options = {}) {
|
|
|
28210
28338
|
console.log("Client disconnected:", socket.id);
|
|
28211
28339
|
});
|
|
28212
28340
|
});
|
|
28213
|
-
const dbFilePath =
|
|
28341
|
+
const dbFilePath = join13(ocrDir, "data", "ocr.db");
|
|
28214
28342
|
const dbSyncWatcher = new DbSyncWatcher(db, dbFilePath, io, () => {
|
|
28215
28343
|
saveDb(db, ocrDir);
|
|
28216
28344
|
});
|
|
@@ -28221,12 +28349,13 @@ async function startServer(options = {}) {
|
|
|
28221
28349
|
() => dbSyncWatcher.syncFromDisk(),
|
|
28222
28350
|
() => dbSyncWatcher.markOwnWrite()
|
|
28223
28351
|
);
|
|
28224
|
-
const sessionsDir =
|
|
28352
|
+
const sessionsDir = join13(ocrDir, "sessions");
|
|
28225
28353
|
const fsSync = new FilesystemSync(db, sessionsDir, io, () => saveDb(db, ocrDir));
|
|
28226
28354
|
await fsSync.fullScan();
|
|
28227
28355
|
saveDb(db, ocrDir);
|
|
28228
28356
|
fsSync.startWatching();
|
|
28229
28357
|
console.log(`Watching sessions: ${sessionsDir}`);
|
|
28358
|
+
const stopReviewersWatch = watchReviewersMeta(ocrDir, io);
|
|
28230
28359
|
await new Promise((resolve3, reject) => {
|
|
28231
28360
|
httpServer.once("error", (err) => {
|
|
28232
28361
|
if (err.code === "EADDRINUSE") {
|
|
@@ -28310,6 +28439,7 @@ async function startServer(options = {}) {
|
|
|
28310
28439
|
}
|
|
28311
28440
|
dbSyncWatcher.stopWatching();
|
|
28312
28441
|
fsSync.stopWatching();
|
|
28442
|
+
stopReviewersWatch();
|
|
28313
28443
|
io.close();
|
|
28314
28444
|
httpServer.close(() => {
|
|
28315
28445
|
try {
|