@nk070281sjv/cli 2.3.26 → 2.3.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +143 -80
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -30775,7 +30775,7 @@ ${hint}
30775
30775
  }
30776
30776
 
30777
30777
  // src/lib/version.ts
30778
- var CLI_VERSION = true ? "2.3.26" : createRequire(import.meta.url)("../../package.json").version;
30778
+ var CLI_VERSION = true ? "2.3.30" : createRequire(import.meta.url)("../../package.json").version;
30779
30779
 
30780
30780
  // src/lib/deps.ts
30781
30781
  init_src();
@@ -35280,6 +35280,7 @@ function validateReviewer(value, path2, errors) {
35280
35280
  stringValue(obj.model, `${path2}.model`, errors);
35281
35281
  stringValue(obj.promptPath, `${path2}.promptPath`, errors);
35282
35282
  stringValue(obj.reviewPath, `${path2}.reviewPath`, errors);
35283
+ stringValue(obj.promptId, `${path2}.promptId`, errors);
35283
35284
  }
35284
35285
  function validatePipelineArtifactRef(value, path2, errors) {
35285
35286
  const obj = object(value, path2, errors);
@@ -37417,6 +37418,7 @@ function extractVendorSessionId(events) {
37417
37418
 
37418
37419
  // src/lib/agent-orchestrator/review-orchestrator.ts
37419
37420
  import { existsSync as existsSync22, statSync as statSync5, readFileSync as readFileSync16 } from "node:fs";
37421
+ import { createHash as createHash2 } from "node:crypto";
37420
37422
  import { join as join27 } from "node:path";
37421
37423
 
37422
37424
  // src/lib/agent-orchestrator/artifact-writer.ts
@@ -37957,6 +37959,7 @@ function isRecord(value) {
37957
37959
 
37958
37960
  // src/lib/agent-orchestrator/prompt-writer.ts
37959
37961
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
37962
+ import { createHash } from "node:crypto";
37960
37963
  import { dirname as dirname11, join as join26, relative as relative3 } from "node:path";
37961
37964
  async function writePromptSnapshots(input) {
37962
37965
  const promptsDir = join26(input.context.roundDir, "prompts");
@@ -37973,9 +37976,10 @@ async function writePromptSnapshots(input) {
37973
37976
  }
37974
37977
  const promptPath = `prompts/${reviewer.persona}-${reviewer.instance_index}.md`;
37975
37978
  const reviewPath = `reviews/${reviewer.persona}-${reviewer.instance_index}.md`;
37979
+ const promptId = reviewerPromptId(input.context, reviewer, promptPath);
37976
37980
  await writeFile3(
37977
37981
  join26(input.context.roundDir, promptPath),
37978
- await reviewerPrompt(input.context, reviewer, promptPath, reviewPath, sharedReviewerPrefix),
37982
+ await reviewerPrompt(input.context, reviewer, promptPath, reviewPath, sharedReviewerPrefix, promptId),
37979
37983
  "utf-8"
37980
37984
  );
37981
37985
  reviewers.push({
@@ -37983,7 +37987,8 @@ async function writePromptSnapshots(input) {
37983
37987
  instance: reviewer.instance_index,
37984
37988
  model: reviewer.model,
37985
37989
  promptPath,
37986
- reviewPath
37990
+ reviewPath,
37991
+ promptId
37987
37992
  });
37988
37993
  }
37989
37994
  const pipeline = {
@@ -38017,7 +38022,7 @@ async function writePipelinePrompt(input, stage, artifactPath) {
38017
38022
  artifactPath
38018
38023
  };
38019
38024
  }
38020
- async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedPrefix) {
38025
+ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedPrefix, promptId) {
38021
38026
  const personaPath = reviewerPersonaPath(context, reviewer);
38022
38027
  const personaContent = await readTextFile(personaPath);
38023
38028
  return [
@@ -38027,6 +38032,7 @@ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedP
38027
38032
  `Reviewer id: ${reviewer.persona}-${reviewer.instance_index}`,
38028
38033
  `Agent identity: ${reviewer.name}`,
38029
38034
  `Model alias and resolved model: ${reviewer.model}`,
38035
+ `OCR prompt id: ${promptId}`,
38030
38036
  "",
38031
38037
  "## Reviewer Persona",
38032
38038
  "",
@@ -38041,8 +38047,9 @@ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedP
38041
38047
  "- Persona focus areas are for detecting issues only; this output contract overrides any persona request to propose fixes or broad guidance.",
38042
38048
  "",
38043
38049
  "Output contract:",
38044
- `- Return review markdown through stdout for ${reviewPath}.`,
38045
- "- Return only one fenced ```ocr-json block and no prose outside it.",
38050
+ `- Return the review artifact through stdout for ${reviewPath}.`,
38051
+ "- Return exactly one fenced ```ocr-json block and no prose outside it.",
38052
+ `- The ocr-json block must include top-level "prompt_id": "${promptId}".`,
38046
38053
  "- Include maximum 5 findings, sorted by severity and production impact.",
38047
38054
  "- No introductions, no progress narration, no endpoint tables, no code snippets, no broad summaries.",
38048
38055
  "- Include critical, high, and medium findings. Include low only when it has concrete runtime/user impact. Exclude info/style/theoretical-only items.",
@@ -38052,10 +38059,11 @@ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedP
38052
38059
  "- Do not claim skipped agents ran.",
38053
38060
  "",
38054
38061
  "Required ocr-json schema:",
38055
- "- Output exactly this compact JSON shape inside the fenced block:",
38062
+ "- Output exactly this compact JSON shape inside a fenced ```ocr-json block:",
38056
38063
  "",
38057
- "```json",
38064
+ "```ocr-json",
38058
38065
  "{",
38066
+ ` "prompt_id": "${promptId}",`,
38059
38067
  ' "findings": [',
38060
38068
  " {",
38061
38069
  ' "sev": "high",',
@@ -38081,6 +38089,18 @@ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedP
38081
38089
  ""
38082
38090
  ].join("\n");
38083
38091
  }
38092
+ function reviewerPromptId(context, reviewer, promptPath) {
38093
+ const input = [
38094
+ context.sessionId,
38095
+ String(context.round),
38096
+ reviewer.persona,
38097
+ String(reviewer.instance_index),
38098
+ reviewer.name,
38099
+ reviewer.model,
38100
+ promptPath
38101
+ ].join("\n");
38102
+ return `ocr-prompt:${createHash("sha256").update(input).digest("hex").slice(0, 24)}`;
38103
+ }
38084
38104
  async function reviewerSharedPrefix(context) {
38085
38105
  const sections = [
38086
38106
  "# OCR Reviewer Shared Context",
@@ -38184,7 +38204,7 @@ function pipelinePrompt(context, stage, agent, promptPath, artifactPath) {
38184
38204
  "- Do not translate JSON field names, enum values, file paths, code identifiers, stack traces, commands, or model names.",
38185
38205
  "- Do not add new findings, remove findings, change severity/category/verdict, or change counts."
38186
38206
  ] : stage === "aggregation" ? [
38187
- "- Return only one fenced ```ocr-json block and no prose outside it.",
38207
+ "- Return exactly one fenced ```ocr-json block and no prose outside it.",
38188
38208
  "- Keep the output compact: at most 8 findings and at most 12 dropped entries.",
38189
38209
  "- Do not include code snippets, long quotes, markdown tables, or reviewer narrative.",
38190
38210
  "- Read the original reviewer files, but aggregate only their structured findings and concrete evidence."
@@ -38219,7 +38239,7 @@ function schemaHint(stage) {
38219
38239
  " - Keep low only when duplicated by 2+ reviewers or when it has concrete production/runtime impact.",
38220
38240
  " - Drop info, style, theoretical-only, and future-extensibility-only items.",
38221
38241
  "",
38222
- "```json",
38242
+ "```ocr-json",
38223
38243
  "{",
38224
38244
  ' "findings": [',
38225
38245
  " {",
@@ -38261,7 +38281,7 @@ function schemaHint(stage) {
38261
38281
  "- Verify claims against the actual code before confirming. Do not trust reviewer text without code evidence.",
38262
38282
  "- Put every aggregation finding into exactly one of `confirmed`, `downgraded`, or `rejected`.",
38263
38283
  "",
38264
- "```json",
38284
+ "```ocr-json",
38265
38285
  "{",
38266
38286
  ' "confirmed": [',
38267
38287
  " {",
@@ -38303,7 +38323,7 @@ function schemaHint(stage) {
38303
38323
  "- Write developer-facing findings from validation results, not from unvalidated reviewer claims.",
38304
38324
  "- Include only confirmed and downgraded issues that a developer can act on.",
38305
38325
  "",
38306
- "```json",
38326
+ "```ocr-json",
38307
38327
  "{",
38308
38328
  ' "verdict": "REQUEST CHANGES",',
38309
38329
  ' "synthesis_counts": {',
@@ -38432,7 +38452,48 @@ async function runReviewerPhase(input) {
38432
38452
  } finally {
38433
38453
  clearInterval(progressTimer);
38434
38454
  }
38435
- const failed = results.find((result) => result.exitCode !== 0);
38455
+ const byId = new Map(input.resolvedTeam.reviewers.map((reviewer) => [
38456
+ `${reviewer.type}-${reviewer.instance}`,
38457
+ reviewer
38458
+ ]));
38459
+ const sortedResults = sortResults(results, requests);
38460
+ const reviewerArtifacts = /* @__PURE__ */ new Map();
38461
+ const artifactStatuses = [];
38462
+ let failed = sortedResults.find((result) => result.exitCode !== 0);
38463
+ let validationFailure;
38464
+ for (const result of sortedResults) {
38465
+ const reviewer = byId.get(result.id);
38466
+ if (!reviewer) continue;
38467
+ if (result.exitCode !== 0) {
38468
+ artifactStatuses.push({
38469
+ id: result.id,
38470
+ exit_code: result.exitCode,
38471
+ artifact_written: false
38472
+ });
38473
+ continue;
38474
+ }
38475
+ const validated = normalizeReviewerArtifact(processOutput(result), reviewer.promptId);
38476
+ if (!validated.ok) {
38477
+ validationFailure ??= { result, errors: validated.errors };
38478
+ artifactStatuses.push({
38479
+ id: result.id,
38480
+ exit_code: 1,
38481
+ artifact_written: false,
38482
+ validation_errors: validated.errors
38483
+ });
38484
+ continue;
38485
+ }
38486
+ reviewerArtifacts.set(reviewer.reviewPath, validated.artifact);
38487
+ artifactStatuses.push({
38488
+ id: result.id,
38489
+ exit_code: result.exitCode,
38490
+ artifact_written: true,
38491
+ artifact_path: join27(input.context.roundDir, reviewer.reviewPath)
38492
+ });
38493
+ }
38494
+ for (const [reviewPath, artifact] of reviewerArtifacts) {
38495
+ await writeRoundArtifact(input.context.roundDir, reviewPath, artifact);
38496
+ }
38436
38497
  if (failed) {
38437
38498
  const failedRequest = requests.find((request) => request.id === failed.id);
38438
38499
  const diagnostic = buildFailureDiagnostic(failed, failedRequest);
@@ -38451,9 +38512,11 @@ async function runReviewerPhase(input) {
38451
38512
  diagnostic,
38452
38513
  stderr: failed.stderr,
38453
38514
  stdout_excerpt: excerpt(failed.stdout),
38454
- results: sortResults(results, requests).map((result) => ({
38455
- id: result.id,
38456
- exit_code: result.exitCode
38515
+ artifact_statuses: artifactStatuses,
38516
+ results: artifactStatuses.map((status) => ({
38517
+ id: status.id,
38518
+ exit_code: status.exit_code,
38519
+ artifact_written: status.artifact_written
38457
38520
  }))
38458
38521
  },
38459
38522
  null,
@@ -38474,71 +38537,54 @@ async function runReviewerPhase(input) {
38474
38537
  ...diagnostic.stderr_excerpt ? [`stderr: ${diagnostic.stderr_excerpt}`] : [],
38475
38538
  ...diagnostic.stdout_excerpt ? [`stdout: ${diagnostic.stdout_excerpt}`] : []
38476
38539
  ],
38477
- results: sortResults(results, requests)
38540
+ results: sortedResults
38478
38541
  };
38479
38542
  }
38480
- const byId = new Map(input.resolvedTeam.reviewers.map((reviewer) => [
38481
- `${reviewer.type}-${reviewer.instance}`,
38482
- reviewer
38483
- ]));
38484
- const reviewerArtifacts = /* @__PURE__ */ new Map();
38485
- for (const result of sortResults(results, requests)) {
38486
- const reviewer = byId.get(result.id);
38487
- if (!reviewer) continue;
38488
- const validated = normalizeReviewerArtifact(processOutput(result));
38489
- if (!validated.ok) {
38490
- const failedResult = { ...result, exitCode: 1 };
38491
- await writeRoundArtifact(
38492
- input.context.roundDir,
38493
- "failure-summary.json",
38494
- JSON.stringify(
38495
- {
38496
- schema_version: 1,
38497
- phase: "reviews",
38498
- failed_id: result.id,
38499
- exit_code: 1,
38500
- model: requests.find((request) => request.id === result.id)?.model,
38501
- prompt_path: requests.find((request) => request.id === result.id)?.promptPath,
38502
- cwd: requests.find((request) => request.id === result.id)?.cwd,
38503
- diagnostic: {
38504
- summary: "Reviewer output did not contain a valid compact ocr-json artifact. OCR refused to pass prose, plan-mode text, or malformed JSON into aggregation.",
38505
- hint: "Inspect the saved reviewer prompt and process log. The reviewer must return exactly one fenced ```ocr-json block with structured findings.",
38506
- stdout_excerpt: excerpt(processOutput(result))
38507
- },
38508
- stderr: result.stderr,
38509
- stdout_excerpt: excerpt(result.stdout),
38510
- validation_errors: validated.errors,
38511
- results: sortResults(
38512
- results.map((item) => item.id === result.id ? failedResult : item),
38513
- requests
38514
- ).map((item) => ({
38515
- id: item.id,
38516
- exit_code: item.exitCode
38517
- }))
38543
+ if (validationFailure) {
38544
+ const failedResult = { ...validationFailure.result, exitCode: 1 };
38545
+ const failureResults = sortedResults.map((item) => item.id === failedResult.id ? failedResult : item);
38546
+ await writeRoundArtifact(
38547
+ input.context.roundDir,
38548
+ "failure-summary.json",
38549
+ JSON.stringify(
38550
+ {
38551
+ schema_version: 1,
38552
+ phase: "reviews",
38553
+ failed_id: validationFailure.result.id,
38554
+ exit_code: 1,
38555
+ model: requests.find((request) => request.id === validationFailure.result.id)?.model,
38556
+ prompt_path: requests.find((request) => request.id === validationFailure.result.id)?.promptPath,
38557
+ cwd: requests.find((request) => request.id === validationFailure.result.id)?.cwd,
38558
+ diagnostic: {
38559
+ summary: "Reviewer output did not contain a valid compact ocr-json artifact. OCR refused to pass prose, plan-mode text, or malformed JSON into aggregation.",
38560
+ hint: "Inspect the saved reviewer prompt and process log. The reviewer must return exactly one fenced ```ocr-json block with structured findings.",
38561
+ stdout_excerpt: excerpt(processOutput(validationFailure.result))
38518
38562
  },
38519
- null,
38520
- 2
38521
- )
38522
- );
38523
- return {
38524
- ok: false,
38525
- failedId: result.id,
38526
- errors: [
38527
- `Reviewer ${result.id} produced invalid ocr-json output.`,
38528
- ...validated.errors
38529
- ],
38530
- results: sortResults(
38531
- results.map((item) => item.id === result.id ? failedResult : item),
38532
- requests
38533
- )
38534
- };
38535
- }
38536
- reviewerArtifacts.set(reviewer.reviewPath, validated.artifact);
38537
- }
38538
- for (const [reviewPath, artifact] of reviewerArtifacts) {
38539
- await writeRoundArtifact(input.context.roundDir, reviewPath, artifact);
38563
+ stderr: validationFailure.result.stderr,
38564
+ stdout_excerpt: excerpt(validationFailure.result.stdout),
38565
+ validation_errors: validationFailure.errors,
38566
+ artifact_statuses: artifactStatuses,
38567
+ results: artifactStatuses.map((status) => ({
38568
+ id: status.id,
38569
+ exit_code: status.exit_code,
38570
+ artifact_written: status.artifact_written
38571
+ }))
38572
+ },
38573
+ null,
38574
+ 2
38575
+ )
38576
+ );
38577
+ return {
38578
+ ok: false,
38579
+ failedId: validationFailure.result.id,
38580
+ errors: [
38581
+ `Reviewer ${validationFailure.result.id} produced invalid ocr-json output.`,
38582
+ ...validationFailure.errors
38583
+ ],
38584
+ results: failureResults
38585
+ };
38540
38586
  }
38541
- return { ok: true, results: sortResults(results, requests) };
38587
+ return { ok: true, results: sortedResults };
38542
38588
  }
38543
38589
  async function runOpenCodeProcessAgentReview(input) {
38544
38590
  const runner = input.runner ?? new OpenCodeProcessRunner();
@@ -38939,6 +38985,7 @@ function isNonEmptyFile(path2) {
38939
38985
  }
38940
38986
  async function writeProcessStartLog(roundDir, request) {
38941
38987
  const metaPath = `process-logs/${request.id}.meta.json`;
38988
+ const promptMeta = readPromptMeta(request.promptPath);
38942
38989
  await writeRoundArtifact(
38943
38990
  roundDir,
38944
38991
  metaPath,
@@ -38949,6 +38996,7 @@ async function writeProcessStartLog(roundDir, request) {
38949
38996
  phase: request.phase,
38950
38997
  model: request.model,
38951
38998
  prompt_path: request.promptPath,
38999
+ ...promptMeta,
38952
39000
  status: "running",
38953
39001
  started_at: (/* @__PURE__ */ new Date()).toISOString()
38954
39002
  },
@@ -38966,6 +39014,7 @@ async function writeProcessLogs(roundDir, request, result) {
38966
39014
  const absoluteMetaPath = join27(roundDir, metaPath);
38967
39015
  const startedAt = readStartedAt(absoluteMetaPath) ?? (/* @__PURE__ */ new Date()).toISOString();
38968
39016
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
39017
+ const promptMeta = readPromptMeta(request.promptPath);
38969
39018
  await writeRoundArtifact(roundDir, stdoutPath, result.stdout);
38970
39019
  await writeRoundArtifact(roundDir, stderrPath, result.stderr);
38971
39020
  await writeRoundArtifact(
@@ -38978,6 +39027,7 @@ async function writeProcessLogs(roundDir, request, result) {
38978
39027
  phase: request.phase,
38979
39028
  model: request.model,
38980
39029
  prompt_path: request.promptPath,
39030
+ ...promptMeta,
38981
39031
  status: result.exitCode === 0 ? "completed" : "failed",
38982
39032
  started_at: startedAt,
38983
39033
  completed_at: completedAt,
@@ -38994,6 +39044,16 @@ async function writeProcessLogs(roundDir, request, result) {
38994
39044
  );
38995
39045
  return { stdout: stdoutPath, stderr: stderrPath, meta: metaPath };
38996
39046
  }
39047
+ function readPromptMeta(promptPath) {
39048
+ if (!existsSync22(promptPath)) return {};
39049
+ const prompt = readFileSync16(promptPath, "utf-8");
39050
+ const bytes = Buffer.byteLength(prompt, "utf-8");
39051
+ return {
39052
+ prompt_sha256: createHash2("sha256").update(prompt).digest("hex"),
39053
+ prompt_bytes: bytes,
39054
+ prompt_delivery: prompt.length <= DEFAULT_PROMPT_ARGV_LIMIT ? "inline" : "file"
39055
+ };
39056
+ }
38997
39057
  function readStartedAt(path2) {
38998
39058
  if (!existsSync22(path2)) return void 0;
38999
39059
  try {
@@ -39007,21 +39067,24 @@ function readStartedAt(path2) {
39007
39067
  function processOutput(result) {
39008
39068
  return result.outputText ?? result.stdout;
39009
39069
  }
39010
- function normalizeReviewerArtifact(markdown) {
39070
+ function normalizeReviewerArtifact(markdown, expectedPromptId) {
39011
39071
  const extracted = extractOcrJsonBlock(markdown);
39012
39072
  if (!extracted.ok) return extracted;
39013
- const validationErrors = validateReviewerJson(extracted.value);
39073
+ const validationErrors = validateReviewerJson(extracted.value, expectedPromptId);
39014
39074
  if (validationErrors.length > 0) return { ok: false, errors: validationErrors };
39015
39075
  const fence = extractOcrJsonFence(markdown);
39016
39076
  if (!fence) return { ok: false, errors: ["Expected exactly one fenced ```ocr-json block."] };
39017
39077
  return { ok: true, artifact: `${fence}
39018
39078
  ` };
39019
39079
  }
39020
- function validateReviewerJson(value) {
39080
+ function validateReviewerJson(value, expectedPromptId) {
39021
39081
  const errors = [];
39022
39082
  if (!isRecord2(value)) {
39023
39083
  return ["Reviewer ocr-json must be an object."];
39024
39084
  }
39085
+ if (value.prompt_id !== expectedPromptId) {
39086
+ errors.push(`Reviewer ocr-json prompt_id must equal ${expectedPromptId}.`);
39087
+ }
39025
39088
  if (!Array.isArray(value.findings)) {
39026
39089
  errors.push("Reviewer ocr-json findings must be an array.");
39027
39090
  return errors;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nk070281sjv/cli",
3
- "version": "2.3.26",
3
+ "version": "2.3.30",
4
4
  "description": "CLI for Open Code Review - Multi-environment setup and progress tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@inquirer/prompts": "^7.2.0",
40
- "@nk070281sjv/agents": "2.3.26",
40
+ "@nk070281sjv/agents": "2.3.30",
41
41
  "chalk": "^5.4.1",
42
42
  "chokidar": "^4.0.3",
43
43
  "commander": "^13.0.0",