@kaitranntt/ccs 7.65.2 → 7.65.3
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/package.json
CHANGED
|
@@ -65,7 +65,9 @@ function cleanMultilineText(value) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function escapeMarkdown(value) {
|
|
68
|
-
return String(value)
|
|
68
|
+
return String(value)
|
|
69
|
+
.replace(/\\/g, '\\\\')
|
|
70
|
+
.replace(/([`*_{}[\]<>|])/g, '\\$1');
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
function escapeMarkdownText(value) {
|
|
@@ -155,7 +157,8 @@ function normalizeRenderingMetadata(raw) {
|
|
|
155
157
|
if (packetIncludedFiles !== null) metadata.packetIncludedFiles = packetIncludedFiles;
|
|
156
158
|
if (packetTotalFiles !== null) metadata.packetTotalFiles = packetTotalFiles;
|
|
157
159
|
if (packetOmittedFiles !== null) metadata.packetOmittedFiles = packetOmittedFiles;
|
|
158
|
-
if (scopeLabel === 'reviewable files' || scopeLabel === 'changed files')
|
|
160
|
+
if (scopeLabel === 'reviewable files' || scopeLabel === 'changed files')
|
|
161
|
+
metadata.scopeLabel = scopeLabel;
|
|
159
162
|
|
|
160
163
|
return metadata;
|
|
161
164
|
}
|
|
@@ -203,7 +206,8 @@ function formatScopeSummary(rendering) {
|
|
|
203
206
|
typeof rendering.selectedChanges === 'number' &&
|
|
204
207
|
typeof rendering.reviewableChanges === 'number'
|
|
205
208
|
) {
|
|
206
|
-
const changeLabel =
|
|
209
|
+
const changeLabel =
|
|
210
|
+
scopeLabel === 'reviewable files' ? 'reviewable changed lines' : 'changed lines';
|
|
207
211
|
return `${fileScope}; ${rendering.selectedChanges}/${rendering.reviewableChanges} ${changeLabel}`;
|
|
208
212
|
}
|
|
209
213
|
|
|
@@ -254,7 +258,8 @@ function formatReviewContext(rendering) {
|
|
|
254
258
|
parts.push(`packet ${rendering.packetIncludedFiles}/${rendering.packetTotalFiles}`);
|
|
255
259
|
}
|
|
256
260
|
|
|
257
|
-
const runtimeBudget =
|
|
261
|
+
const runtimeBudget =
|
|
262
|
+
formatCombinedBudget(rendering) || formatTimeBudget(rendering) || formatTurnBudget(rendering);
|
|
258
263
|
if (runtimeBudget) {
|
|
259
264
|
parts.push(runtimeBudget);
|
|
260
265
|
}
|
|
@@ -301,7 +306,10 @@ function describeIncompleteOutcome({ reason, rendering, turnsUsed, status }) {
|
|
|
301
306
|
return `The ${reviewLabel} ended before it could produce validated structured output within the available ${combinedBudget} runtime budget.`;
|
|
302
307
|
}
|
|
303
308
|
|
|
304
|
-
if (
|
|
309
|
+
if (
|
|
310
|
+
classifyFallbackReason(reason) === 'missing' ||
|
|
311
|
+
classifyFallbackReason(reason) === 'invalid_json'
|
|
312
|
+
) {
|
|
305
313
|
return `The ${reviewLabel} ended without validated structured output, so the normalizer published the safe fallback comment instead.`;
|
|
306
314
|
}
|
|
307
315
|
|
|
@@ -344,7 +352,10 @@ function normalizeChecklistRows(fieldName, labelField, raw) {
|
|
|
344
352
|
|
|
345
353
|
const rows = [];
|
|
346
354
|
for (const [index, item] of raw.entries()) {
|
|
347
|
-
const label = validatePlainTextField(
|
|
355
|
+
const label = validatePlainTextField(
|
|
356
|
+
`${fieldName}[${index}].${labelField}`,
|
|
357
|
+
item?.[labelField]
|
|
358
|
+
);
|
|
348
359
|
if (!label.ok) return label;
|
|
349
360
|
|
|
350
361
|
const notes = validatePlainTextField(`${fieldName}[${index}].notes`, item?.notes);
|
|
@@ -479,16 +490,19 @@ function formatHotspotFiles(files) {
|
|
|
479
490
|
function formatRemainingCoverage(rendering) {
|
|
480
491
|
if (
|
|
481
492
|
typeof rendering.reviewableFiles !== 'number' ||
|
|
482
|
-
(typeof rendering.packetIncludedFiles !== 'number' &&
|
|
493
|
+
(typeof rendering.packetIncludedFiles !== 'number' &&
|
|
494
|
+
typeof rendering.selectedFiles !== 'number')
|
|
483
495
|
) {
|
|
484
496
|
return null;
|
|
485
497
|
}
|
|
486
498
|
|
|
487
|
-
const coveredFiles =
|
|
488
|
-
|
|
489
|
-
|
|
499
|
+
const coveredFiles =
|
|
500
|
+
typeof rendering.packetIncludedFiles === 'number'
|
|
501
|
+
? rendering.packetIncludedFiles
|
|
502
|
+
: rendering.selectedFiles;
|
|
490
503
|
const remainingFiles = Math.max(rendering.reviewableFiles - coveredFiles, 0);
|
|
491
|
-
const packetOmittedFiles =
|
|
504
|
+
const packetOmittedFiles =
|
|
505
|
+
typeof rendering.packetOmittedFiles === 'number' ? rendering.packetOmittedFiles : 0;
|
|
492
506
|
const hasChangeCounts =
|
|
493
507
|
packetOmittedFiles === 0 &&
|
|
494
508
|
typeof rendering.selectedChanges === 'number' &&
|
|
@@ -540,7 +554,11 @@ export function normalizeStructuredOutput(raw) {
|
|
|
540
554
|
if (!overallRationale.ok) return overallRationale;
|
|
541
555
|
|
|
542
556
|
const findings = Array.isArray(parsed.findings) ? parsed.findings : null;
|
|
543
|
-
const securityChecklist = normalizeChecklistRows(
|
|
557
|
+
const securityChecklist = normalizeChecklistRows(
|
|
558
|
+
'securityChecklist',
|
|
559
|
+
'check',
|
|
560
|
+
parsed.securityChecklist
|
|
561
|
+
);
|
|
544
562
|
if (!securityChecklist.ok) return securityChecklist;
|
|
545
563
|
|
|
546
564
|
const ccsCompliance = normalizeChecklistRows('ccsCompliance', 'rule', parsed.ccsCompliance);
|
|
@@ -582,7 +600,11 @@ export function normalizeStructuredOutput(raw) {
|
|
|
582
600
|
if (finding && Object.hasOwn(finding, 'line')) {
|
|
583
601
|
if (finding.line === null) {
|
|
584
602
|
line = null;
|
|
585
|
-
} else if (
|
|
603
|
+
} else if (
|
|
604
|
+
typeof finding.line === 'number' &&
|
|
605
|
+
Number.isInteger(finding.line) &&
|
|
606
|
+
finding.line > 0
|
|
607
|
+
) {
|
|
586
608
|
line = finding.line;
|
|
587
609
|
} else {
|
|
588
610
|
return { ok: false, reason: `findings[${index}].line is invalid` };
|
|
@@ -668,7 +690,9 @@ function renderFindingReference(finding) {
|
|
|
668
690
|
}
|
|
669
691
|
|
|
670
692
|
function getOrderedFindings(findings) {
|
|
671
|
-
return SEVERITY_ORDER.flatMap((severity) =>
|
|
693
|
+
return SEVERITY_ORDER.flatMap((severity) =>
|
|
694
|
+
findings.filter((finding) => finding.severity === severity)
|
|
695
|
+
);
|
|
672
696
|
}
|
|
673
697
|
|
|
674
698
|
function renderTopFindings(findings) {
|
|
@@ -726,37 +750,65 @@ function renderDetailedFindings(findings) {
|
|
|
726
750
|
|
|
727
751
|
export function renderStructuredReview(review, { model, rendering: renderOptions } = {}) {
|
|
728
752
|
const rendering = mergeRenderingMetadata(review?.rendering, renderOptions);
|
|
729
|
-
const lines = [
|
|
730
|
-
'### Verdict',
|
|
731
|
-
'',
|
|
732
|
-
`**${ASSESSMENTS[review.overallAssessment]}** — ${renderInlineText(review.overallRationale)}`,
|
|
733
|
-
'',
|
|
734
|
-
renderInlineText(review.summary),
|
|
735
|
-
];
|
|
753
|
+
const lines = ['### 📋 Summary', '', renderInlineText(review.summary)];
|
|
736
754
|
const reviewContext = formatReviewContext(rendering);
|
|
737
755
|
|
|
738
756
|
if (reviewContext) {
|
|
739
757
|
lines.push('', reviewContext);
|
|
740
758
|
}
|
|
741
759
|
|
|
742
|
-
lines.push('', '###
|
|
743
|
-
|
|
760
|
+
lines.push('', '### 🔍 Findings');
|
|
761
|
+
if (review.findings.length === 0) {
|
|
762
|
+
lines.push('', 'No confirmed issues found after reviewing the diff and surrounding code.');
|
|
763
|
+
} else {
|
|
764
|
+
for (const severity of SEVERITY_ORDER) {
|
|
765
|
+
const scopedFindings = review.findings.filter((finding) => finding.severity === severity);
|
|
766
|
+
if (scopedFindings.length === 0) continue;
|
|
767
|
+
|
|
768
|
+
lines.push('', SEVERITY_HEADERS[severity], '');
|
|
769
|
+
for (const finding of scopedFindings) {
|
|
770
|
+
const snippets = Array.isArray(finding.snippets) ? finding.snippets : [];
|
|
771
|
+
lines.push(
|
|
772
|
+
`- **${renderCode(renderFindingReference(finding))} — ${renderInlineText(finding.title)}**`
|
|
773
|
+
);
|
|
774
|
+
lines.push(` Problem: ${renderInlineText(finding.what)}`);
|
|
775
|
+
lines.push(` Why it matters: ${renderInlineText(finding.why)}`);
|
|
776
|
+
lines.push(` Suggested fix: ${renderInlineText(finding.fix)}`);
|
|
777
|
+
|
|
778
|
+
if (snippets.length > 0) {
|
|
779
|
+
lines.push('');
|
|
780
|
+
lines.push(...renderFindingSnippets(snippets));
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
lines.push('');
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (lines[lines.length - 1] === '') {
|
|
788
|
+
lines.pop();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
744
792
|
lines.push(
|
|
745
793
|
...renderSection(
|
|
746
|
-
|
|
794
|
+
'### 🔒 Security Checklist',
|
|
747
795
|
renderChecklistTable('Check', 'check', review.securityChecklist)
|
|
748
796
|
)
|
|
749
797
|
);
|
|
750
798
|
lines.push(
|
|
751
799
|
...renderSection(
|
|
752
|
-
|
|
800
|
+
'### 📊 CCS Compliance',
|
|
753
801
|
renderChecklistTable('Rule', 'rule', review.ccsCompliance)
|
|
754
802
|
)
|
|
755
803
|
);
|
|
756
|
-
lines.push(...renderSection(
|
|
757
|
-
lines.push(...renderSection(
|
|
804
|
+
lines.push(...renderSection('### 💡 Informational', renderBulletSection(review.informational)));
|
|
805
|
+
lines.push(...renderSection("### ✅ What's Done Well", renderBulletSection(review.strengths)));
|
|
758
806
|
|
|
759
807
|
lines.push(
|
|
808
|
+
'',
|
|
809
|
+
'### 🎯 Overall Assessment',
|
|
810
|
+
'',
|
|
811
|
+
`**${ASSESSMENTS[review.overallAssessment]}** — ${renderInlineText(review.overallRationale)}`,
|
|
760
812
|
'',
|
|
761
813
|
`> 🤖 Reviewed by \`${model}\``
|
|
762
814
|
);
|
|
@@ -784,7 +836,9 @@ export function renderIncompleteReview({
|
|
|
784
836
|
];
|
|
785
837
|
|
|
786
838
|
if (rendering.mode) {
|
|
787
|
-
lines.push(
|
|
839
|
+
lines.push(
|
|
840
|
+
`- Review mode: ${renderCode(rendering.mode)} (${escapeMarkdownText(REVIEW_MODE_DETAILS[rendering.mode])})`
|
|
841
|
+
);
|
|
788
842
|
}
|
|
789
843
|
const scopeSummary = formatScopeSummary(rendering);
|
|
790
844
|
if (scopeSummary) {
|
|
@@ -804,7 +858,9 @@ export function renderIncompleteReview({
|
|
|
804
858
|
}
|
|
805
859
|
const remainingCoverage = formatRemainingCoverage(rendering);
|
|
806
860
|
if (remainingCoverage) {
|
|
807
|
-
lines.push(
|
|
861
|
+
lines.push(
|
|
862
|
+
`- Remaining reviewable scope not fully covered: ${escapeMarkdownText(remainingCoverage)}`
|
|
863
|
+
);
|
|
808
864
|
}
|
|
809
865
|
lines.push(`- Manual follow-up: ${escapeMarkdownText(formatFallbackFollowUp(rendering))}`);
|
|
810
866
|
if (runtimeTools?.length) {
|
|
@@ -814,7 +870,12 @@ export function renderIncompleteReview({
|
|
|
814
870
|
lines.push(`- Turns used: ${turnsUsed}`);
|
|
815
871
|
}
|
|
816
872
|
|
|
817
|
-
lines.push(
|
|
873
|
+
lines.push(
|
|
874
|
+
'',
|
|
875
|
+
`Re-run \`/review\` or inspect [the workflow run](${runUrl}).`,
|
|
876
|
+
'',
|
|
877
|
+
`> 🤖 Reviewed by \`${model}\``
|
|
878
|
+
);
|
|
818
879
|
return lines.join('\n');
|
|
819
880
|
}
|
|
820
881
|
|
|
@@ -857,15 +918,16 @@ export function writeReviewFromEnv(env = process.env) {
|
|
|
857
918
|
fs.writeFileSync(outputFile, `${content}\n`, 'utf8');
|
|
858
919
|
|
|
859
920
|
if (!validation.ok) {
|
|
860
|
-
console.warn(
|
|
921
|
+
console.warn(
|
|
922
|
+
`::warning::AI review output normalization fell back to incomplete comment: ${validation.reason}`
|
|
923
|
+
);
|
|
861
924
|
}
|
|
862
925
|
|
|
863
926
|
return { usedFallback: !validation.ok, content };
|
|
864
927
|
}
|
|
865
928
|
|
|
866
929
|
const isMain =
|
|
867
|
-
process.argv[1] &&
|
|
868
|
-
path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
930
|
+
process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
869
931
|
|
|
870
932
|
if (isMain) {
|
|
871
933
|
writeReviewFromEnv();
|