@clearlydev/nx-archweave 0.3.0 → 0.3.1

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.
@@ -84,7 +84,7 @@ function normalizeFinding(raw) {
84
84
  // ../review-policy/dist/renderers/render-review-comment.js
85
85
  var COPY = {
86
86
  en: {
87
- terminationBanner: (maxRounds) => `> \u26A0\uFE0F **Automated review ended.** The maximum of ${maxRounds} review rounds has been reached with blocking findings (P0/P1) still open. Human review is required.`,
87
+ terminationBanner: (maxRounds) => `> \u26A0\uFE0F **Automated review ended.** The maximum of ${maxRounds} review rounds has been reached with blocking findings (P0/P1) still open. Later pushes aren't reviewed by AI, so review them yourself before merging.`,
88
88
  verdictLines: {
89
89
  BLOCK: "**Verdict: BLOCK** \u2014 P0/P1 findings must be resolved before this change can merge.",
90
90
  CHANGES_RECOMMENDED: "**Verdict: CHANGES_RECOMMENDED** \u2014 P2 findings should be addressed.",
@@ -106,7 +106,7 @@ var COPY = {
106
106
  }
107
107
  },
108
108
  ko: {
109
- terminationBanner: (maxRounds) => `> \u26A0\uFE0F **\uC790\uB3D9 \uB9AC\uBDF0 \uC885\uB8CC.** \uCD5C\uB300 ${maxRounds}\uD68C\uC758 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC\uC5D0 \uB3C4\uB2EC\uD588\uC9C0\uB9CC \uBCD1\uD569\uC744 \uB9C9\uB294 finding(P0/P1)\uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uC0AC\uB78C\uC758 \uB9AC\uBDF0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.`,
109
+ terminationBanner: (maxRounds) => `> \u26A0\uFE0F **\uC790\uB3D9 \uB9AC\uBDF0 \uC885\uB8CC.** \uCD5C\uB300 ${maxRounds}\uD68C\uC758 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC\uC5D0 \uB3C4\uB2EC\uD588\uC9C0\uB9CC \uBCD1\uD569\uC744 \uB9C9\uB294 finding(P0/P1)\uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uC774\uD6C4 push\uB294 AI\uAC00 \uB9AC\uBDF0\uD558\uC9C0 \uC54A\uC73C\uB2C8, \uBCD1\uD569 \uC804\uC5D0 \uC9C1\uC811 \uB9AC\uBDF0\uD558\uC138\uC694.`,
110
110
  verdictLines: {
111
111
  BLOCK: "**\uD310\uC815: BLOCK** \u2014 \uBCD1\uD569 \uC804\uC5D0 P0/P1 finding\uC744 \uD574\uACB0\uD574\uC57C \uD569\uB2C8\uB2E4.",
112
112
  CHANGES_RECOMMENDED: "**\uD310\uC815: CHANGES_RECOMMENDED** \u2014 P2 finding\uC744 \uD574\uACB0\uD558\uB294 \uAC83\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
@@ -200,6 +200,14 @@ var ROUND_MARKER = "<!-- archweave-review-round -->";
200
200
  function collectRounds(comments) {
201
201
  return comments.filter((c) => c.body.includes(ROUND_MARKER) && !c.body.includes(VERDICT_MARKER)).map((c, index) => ({ round: index + 1, id: c.id, extraction: extractFindingsBlock(c.body) }));
202
202
  }
203
+ var LINE_DRIFT = 3;
204
+ function sameDefect(a, b) {
205
+ if (a.file !== b.file)
206
+ return false;
207
+ const aEnd = a.lineRange.end ?? a.lineRange.start;
208
+ const bEnd = b.lineRange.end ?? b.lineRange.start;
209
+ return a.lineRange.start <= bEnd + LINE_DRIFT && b.lineRange.start <= aEnd + LINE_DRIFT;
210
+ }
203
211
  function computeRoundHistory(rounds) {
204
212
  const parsed = rounds.filter((r) => r.extraction.status === "ok");
205
213
  const history = [];
@@ -209,10 +217,10 @@ function computeRoundHistory(rounds) {
209
217
  history.push({ round: parsed[i].round, fixedFindingKeys: [], remainingFindingKeys: [...keys] });
210
218
  continue;
211
219
  }
212
- const previous = new Set(parsed[i - 1].extraction.findings.map(findingKey));
220
+ const current = parsed[i].extraction.findings;
213
221
  history.push({
214
222
  round: parsed[i].round,
215
- fixedFindingKeys: [...previous].filter((key) => !keys.has(key)),
223
+ fixedFindingKeys: parsed[i - 1].extraction.findings.filter((finding) => !current.some((now) => sameDefect(finding, now))).map(findingKey),
216
224
  remainingFindingKeys: [...keys]
217
225
  });
218
226
  }
@@ -252,12 +260,20 @@ var realWriteOutput = (path, text) => appendFileSync(path, text);
252
260
  function outputModePath(env) {
253
261
  return env["ARCHWEAVE_VERDICT_OUTPUT"] === "github-output" && env["GITHUB_OUTPUT"] ? env["GITHUB_OUTPUT"] : void 0;
254
262
  }
255
- function renderStepOutputs(commentBody, previousId, blocked) {
263
+ function renderStepOutputs(commentBody, previousId, blocked, reason) {
256
264
  let delimiter;
257
265
  do
258
266
  delimiter = `ARCHWEAVE_${randomBytes(8).toString("hex")}`;
259
267
  while (commentBody.split("\n").includes(delimiter));
260
- return [`comment-body<<${delimiter}`, commentBody, delimiter, `previous-comment-id=${previousId ?? ""}`, `blocked=${blocked}`, ""].join("\n");
268
+ return [
269
+ `comment-body<<${delimiter}`,
270
+ commentBody,
271
+ delimiter,
272
+ `previous-comment-id=${previousId ?? ""}`,
273
+ `blocked=${blocked}`,
274
+ `block-reason=${blocked ? reason ?? "" : ""}`,
275
+ ""
276
+ ].join("\n");
261
277
  }
262
278
  var UNREAD_COMMENTS_ERROR = "::error title=Review verdict not computed::Could not read PR comments, so the verdict could not be computed. Re-run the failed jobs to retry. If it fails again, check that the review job has pull-requests: read.";
263
279
  function runReviewVerdictBin(env, exec, writeOutput = realWriteOutput) {
@@ -298,7 +314,8 @@ function runReviewVerdictBin(env, exec, writeOutput = realWriteOutput) {
298
314
  const settled = !failedReview && (result.kind === "no-round" || result.kind === "unparseable");
299
315
  const blocked = failedReview || result.kind === "verdict" && result.exitCode !== 0;
300
316
  if (outputPath !== void 0) {
301
- writeOutput(outputPath, renderStepOutputs(result.commentBody, previous?.id, blocked));
317
+ const reason = result.kind === "no-summary" ? "no-summary" : result.kind === "unparseable" ? "unreadable" : "block";
318
+ writeOutput(outputPath, renderStepOutputs(result.commentBody, previous?.id, blocked, reason));
302
319
  } else {
303
320
  const payload = JSON.stringify({ body: result.commentBody });
304
321
  const updated = previous !== void 0 && exec("gh", ["api", "-X", "PATCH", `repos/${repo}/issues/comments/${previous.id}`, "--input", "-"], payload).status === 0;
@@ -347,10 +347,17 @@ function renderVerdictGateStep(maxRounds, locale) {
347
347
  }
348
348
  function renderBlockStep() {
349
349
  return [
350
- "- name: Fail on a blocking verdict",
350
+ "- name: Fail the review check",
351
351
  " if: steps.verdict.outputs.blocked == 'true'",
352
+ " env:",
353
+ " BLOCK_REASON: ${{ steps.verdict.outputs.block-reason }}",
352
354
  " run: |",
353
- ' echo "The review check failed: the verdict is BLOCK, or the AI review left no readable summary. See the verdict comment on this pull request."',
355
+ ' case "$BLOCK_REASON" in',
356
+ ' block) echo "The AI verdict is BLOCK: fix the P0/P1 findings in the verdict comment and push." ;;',
357
+ ' no-summary) echo "The AI review posted no summary for this commit, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
358
+ ' unreadable) echo "The AI review summary has no readable findings block, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
359
+ ' *) echo "The review check failed. See the verdict comment on this pull request." ;;',
360
+ " esac",
354
361
  " exit 1"
355
362
  ].join("\n");
356
363
  }
@@ -547,7 +554,7 @@ function renderDeniedToolsStep() {
547
554
  ' [ -f "$EXECUTION_FILE" ] || exit 0',
548
555
  ` stop_token="archweave-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')"`,
549
556
  ' echo "::stop-commands::$stop_token"',
550
- ` jq -c '.[] | select(.type == "result") | .permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
557
+ ` jq -c '.[] | select(.type == "result") | ((.permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}), (select(.is_error == true) | {error: ((.result // "") | tostring | .[0:300])}))' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
551
558
  ' echo "::$stop_token::"'
552
559
  ].join("\n");
553
560
  }
@@ -490,10 +490,17 @@ function renderVerdictGateStep(maxRounds, locale) {
490
490
  }
491
491
  function renderBlockStep() {
492
492
  return [
493
- "- name: Fail on a blocking verdict",
493
+ "- name: Fail the review check",
494
494
  " if: steps.verdict.outputs.blocked == 'true'",
495
+ " env:",
496
+ " BLOCK_REASON: ${{ steps.verdict.outputs.block-reason }}",
495
497
  " run: |",
496
- ' echo "The review check failed: the verdict is BLOCK, or the AI review left no readable summary. See the verdict comment on this pull request."',
498
+ ' case "$BLOCK_REASON" in',
499
+ ' block) echo "The AI verdict is BLOCK: fix the P0/P1 findings in the verdict comment and push." ;;',
500
+ ' no-summary) echo "The AI review posted no summary for this commit, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
501
+ ' unreadable) echo "The AI review summary has no readable findings block, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
502
+ ' *) echo "The review check failed. See the verdict comment on this pull request." ;;',
503
+ " esac",
497
504
  " exit 1"
498
505
  ].join("\n");
499
506
  }
@@ -690,7 +697,7 @@ function renderDeniedToolsStep() {
690
697
  ' [ -f "$EXECUTION_FILE" ] || exit 0',
691
698
  ` stop_token="archweave-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')"`,
692
699
  ' echo "::stop-commands::$stop_token"',
693
- ` jq -c '.[] | select(.type == "result") | .permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
700
+ ` jq -c '.[] | select(.type == "result") | ((.permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}), (select(.is_error == true) | {error: ((.result // "") | tostring | .[0:300])}))' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
694
701
  ' echo "::$stop_token::"'
695
702
  ].join("\n");
696
703
  }
@@ -442,10 +442,17 @@ function renderVerdictGateStep(maxRounds, locale) {
442
442
  }
443
443
  function renderBlockStep() {
444
444
  return [
445
- "- name: Fail on a blocking verdict",
445
+ "- name: Fail the review check",
446
446
  " if: steps.verdict.outputs.blocked == 'true'",
447
+ " env:",
448
+ " BLOCK_REASON: ${{ steps.verdict.outputs.block-reason }}",
447
449
  " run: |",
448
- ' echo "The review check failed: the verdict is BLOCK, or the AI review left no readable summary. See the verdict comment on this pull request."',
450
+ ' case "$BLOCK_REASON" in',
451
+ ' block) echo "The AI verdict is BLOCK: fix the P0/P1 findings in the verdict comment and push." ;;',
452
+ ' no-summary) echo "The AI review posted no summary for this commit, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
453
+ ' unreadable) echo "The AI review summary has no readable findings block, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
454
+ ' *) echo "The review check failed. See the verdict comment on this pull request." ;;',
455
+ " esac",
449
456
  " exit 1"
450
457
  ].join("\n");
451
458
  }
@@ -642,7 +649,7 @@ function renderDeniedToolsStep() {
642
649
  ' [ -f "$EXECUTION_FILE" ] || exit 0',
643
650
  ` stop_token="archweave-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')"`,
644
651
  ' echo "::stop-commands::$stop_token"',
645
- ` jq -c '.[] | select(.type == "result") | .permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
652
+ ` jq -c '.[] | select(.type == "result") | ((.permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}), (select(.is_error == true) | {error: ((.result // "") | tostring | .[0:300])}))' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
646
653
  ' echo "::$stop_token::"'
647
654
  ].join("\n");
648
655
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@clearlydev/nx-archweave",
3
- "version": "0.3.0",
4
- "description": "Nx generators that make a workspace ready to work on in one preset: an FSD Lite lint rule, frontend/backend boundaries, AI agent rules, GitHub templates, and optional AI PR review with the provider you choose (Claude supported; Gemini, GPT and Cursor in beta)",
3
+ "version": "0.3.1",
4
+ "description": "Nx generators for an FSD Lite lint rule, frontend/backend boundaries, AI agent rules, GitHub templates and optional AI PR review",
5
5
  "license": "MIT",
6
6
  "author": "clearlydev (https://github.com/clearlydev)",
7
7
  "engines": {