@nk070281sjv/cli 2.3.23 → 2.3.25

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 +108 -5
  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.23" : createRequire(import.meta.url)("../../package.json").version;
30778
+ var CLI_VERSION = true ? "2.3.25" : createRequire(import.meta.url)("../../package.json").version;
30779
30779
 
30780
30780
  // src/lib/deps.ts
30781
30781
  init_src();
@@ -38039,14 +38039,15 @@ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedP
38039
38039
  "- Use the review brief as the source of truth for changed files and scope.",
38040
38040
  "- Inspect surrounding source code only when needed to validate a finding.",
38041
38041
  "- Rank by concrete risk: data loss, wrong persisted state, broken public contract, security, production performance, then testability.",
38042
+ "- Persona focus areas are for detecting issues only; this output contract overrides any persona request to propose fixes or broad guidance.",
38042
38043
  "",
38043
38044
  "Output contract:",
38044
38045
  `- Return review markdown through stdout for ${reviewPath}.`,
38045
38046
  "- Return only one fenced ```ocr-json block and no prose outside it.",
38046
- "- Include maximum 10 findings, sorted by severity and production impact.",
38047
+ "- Include maximum 5 findings, sorted by severity and production impact.",
38047
38048
  "- No introductions, no progress narration, no endpoint tables, no code snippets, no broad summaries.",
38048
38049
  "- Include critical, high, and medium findings. Include low only when it has concrete runtime/user impact. Exclude info/style/theoretical-only items.",
38049
- "- Do not include fix instructions; aggregation and synthesis will produce fix direction after deduplication and validation.",
38050
+ "- Do not write fix instructions, suggested patches, implementation recipes, or remediation plans.",
38050
38051
  "- Exclude any item that does not have concrete `files` and `evidence`.",
38051
38052
  "- Do not write files directly.",
38052
38053
  "- Do not claim skipped agents ran.",
@@ -38072,7 +38073,8 @@ async function reviewerPrompt(context, reviewer, promptPath, reviewPath, sharedP
38072
38073
  "",
38073
38074
  '- `sev` must be one of: "critical", "high", "medium", "low".',
38074
38075
  '- `confidence` must be one of: "high", "medium", "low".',
38075
- "- Keep every string under roughly 240 characters.",
38076
+ "- Keep title/claim/impact under roughly 180 characters each.",
38077
+ "- Keep evidence under roughly 260 characters and cite only the strongest code signal.",
38076
38078
  "",
38077
38079
  "Forbidden behavior:",
38078
38080
  `- Do not modify ${promptPath} or ${reviewPath}.`,
@@ -38480,10 +38482,62 @@ async function runReviewerPhase(input) {
38480
38482
  `${reviewer.type}-${reviewer.instance}`,
38481
38483
  reviewer
38482
38484
  ]));
38485
+ const reviewerArtifacts = /* @__PURE__ */ new Map();
38483
38486
  for (const result of sortResults(results, requests)) {
38484
38487
  const reviewer = byId.get(result.id);
38485
38488
  if (!reviewer) continue;
38486
- await writeRoundArtifact(input.context.roundDir, reviewer.reviewPath, processOutput(result));
38489
+ const validated = normalizeReviewerArtifact(processOutput(result));
38490
+ if (!validated.ok) {
38491
+ const failedResult = { ...result, exitCode: 1 };
38492
+ await writeRoundArtifact(
38493
+ input.context.roundDir,
38494
+ "failure-summary.json",
38495
+ JSON.stringify(
38496
+ {
38497
+ schema_version: 1,
38498
+ phase: "reviews",
38499
+ failed_id: result.id,
38500
+ exit_code: 1,
38501
+ model: requests.find((request) => request.id === result.id)?.model,
38502
+ prompt_path: requests.find((request) => request.id === result.id)?.promptPath,
38503
+ cwd: requests.find((request) => request.id === result.id)?.cwd,
38504
+ diagnostic: {
38505
+ 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.",
38506
+ hint: "Inspect the saved reviewer prompt and process log. The reviewer must return exactly one fenced ```ocr-json block with structured findings.",
38507
+ stdout_excerpt: excerpt(processOutput(result))
38508
+ },
38509
+ stderr: result.stderr,
38510
+ stdout_excerpt: excerpt(result.stdout),
38511
+ validation_errors: validated.errors,
38512
+ results: sortResults(
38513
+ results.map((item) => item.id === result.id ? failedResult : item),
38514
+ requests
38515
+ ).map((item) => ({
38516
+ id: item.id,
38517
+ exit_code: item.exitCode
38518
+ }))
38519
+ },
38520
+ null,
38521
+ 2
38522
+ )
38523
+ );
38524
+ return {
38525
+ ok: false,
38526
+ failedId: result.id,
38527
+ errors: [
38528
+ `Reviewer ${result.id} produced invalid ocr-json output.`,
38529
+ ...validated.errors
38530
+ ],
38531
+ results: sortResults(
38532
+ results.map((item) => item.id === result.id ? failedResult : item),
38533
+ requests
38534
+ )
38535
+ };
38536
+ }
38537
+ reviewerArtifacts.set(reviewer.reviewPath, validated.artifact);
38538
+ }
38539
+ for (const [reviewPath, artifact] of reviewerArtifacts) {
38540
+ await writeRoundArtifact(input.context.roundDir, reviewPath, artifact);
38487
38541
  }
38488
38542
  return { ok: true, results: sortResults(results, requests) };
38489
38543
  }
@@ -38954,6 +39008,55 @@ function readStartedAt(path2) {
38954
39008
  function processOutput(result) {
38955
39009
  return result.outputText ?? result.stdout;
38956
39010
  }
39011
+ function normalizeReviewerArtifact(markdown) {
39012
+ const extracted = extractOcrJsonBlock(markdown);
39013
+ if (!extracted.ok) return extracted;
39014
+ const validationErrors = validateReviewerJson(extracted.value);
39015
+ if (validationErrors.length > 0) return { ok: false, errors: validationErrors };
39016
+ const fence = extractOcrJsonFence(markdown);
39017
+ if (!fence) return { ok: false, errors: ["Expected exactly one fenced ```ocr-json block."] };
39018
+ return { ok: true, artifact: `${fence}
39019
+ ` };
39020
+ }
39021
+ function validateReviewerJson(value) {
39022
+ const errors = [];
39023
+ if (!isRecord2(value)) {
39024
+ return ["Reviewer ocr-json must be an object."];
39025
+ }
39026
+ if (!Array.isArray(value.findings)) {
39027
+ errors.push("Reviewer ocr-json findings must be an array.");
39028
+ return errors;
39029
+ }
39030
+ if (value.findings.length > 5) {
39031
+ errors.push("Reviewer ocr-json findings must contain at most 5 items.");
39032
+ }
39033
+ value.findings.forEach((finding, index) => {
39034
+ if (!isRecord2(finding)) {
39035
+ errors.push(`findings[${index}] must be an object.`);
39036
+ return;
39037
+ }
39038
+ for (const field of ["sev", "title", "claim", "evidence", "impact", "confidence"]) {
39039
+ if (typeof finding[field] !== "string" || !finding[field].trim()) {
39040
+ errors.push(`findings[${index}].${field} must be a non-empty string.`);
39041
+ }
39042
+ }
39043
+ if (!["critical", "high", "medium", "low"].includes(String(finding.sev))) {
39044
+ errors.push(`findings[${index}].sev must be one of critical, high, medium, low.`);
39045
+ }
39046
+ if (!["high", "medium", "low"].includes(String(finding.confidence))) {
39047
+ errors.push(`findings[${index}].confidence must be one of high, medium, low.`);
39048
+ }
39049
+ if (!Array.isArray(finding.files) || finding.files.length === 0) {
39050
+ errors.push(`findings[${index}].files must be a non-empty array.`);
39051
+ } else if (finding.files.some((file) => typeof file !== "string" || !file.trim())) {
39052
+ errors.push(`findings[${index}].files must contain only non-empty strings.`);
39053
+ }
39054
+ if ("fix" in finding) {
39055
+ errors.push(`findings[${index}].fix is not allowed in reviewer output.`);
39056
+ }
39057
+ });
39058
+ return errors;
39059
+ }
38957
39060
  function normalizeProcessResult(result) {
38958
39061
  if (!hasOpenCodeLengthFinish(result.ndjsonEvents)) return result;
38959
39062
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nk070281sjv/cli",
3
- "version": "2.3.23",
3
+ "version": "2.3.25",
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.23",
40
+ "@nk070281sjv/agents": "2.3.25",
41
41
  "chalk": "^5.4.1",
42
42
  "chokidar": "^4.0.3",
43
43
  "commander": "^13.0.0",