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