projscan 4.9.3 → 4.10.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.
Files changed (178) hide show
  1. package/README.md +23 -1
  2. package/dist/analyzers/securityCheck.js +33 -15
  3. package/dist/analyzers/securityCheck.js.map +1 -1
  4. package/dist/analyzers/supplyChainCheck.js +9 -2
  5. package/dist/analyzers/supplyChainCheck.js.map +1 -1
  6. package/dist/cli/commands/bugHunt.js +3 -1
  7. package/dist/cli/commands/bugHunt.js.map +1 -1
  8. package/dist/cli/commands/ci.js +29 -13
  9. package/dist/cli/commands/ci.js.map +1 -1
  10. package/dist/cli/commands/dogfood.js +2 -0
  11. package/dist/cli/commands/dogfood.js.map +1 -1
  12. package/dist/cli/commands/feedback.js +21 -2
  13. package/dist/cli/commands/feedback.js.map +1 -1
  14. package/dist/cli/commands/init.js +3 -0
  15. package/dist/cli/commands/init.js.map +1 -1
  16. package/dist/cli/commands/route.js +3 -2
  17. package/dist/cli/commands/route.js.map +1 -1
  18. package/dist/core/adoption.js +50 -21
  19. package/dist/core/adoption.js.map +1 -1
  20. package/dist/core/agentBrief.js +2 -1
  21. package/dist/core/agentBrief.js.map +1 -1
  22. package/dist/core/bugHunt.js +51 -22
  23. package/dist/core/bugHunt.js.map +1 -1
  24. package/dist/core/bugHuntHotspotFindings.js +2 -1
  25. package/dist/core/bugHuntHotspotFindings.js.map +1 -1
  26. package/dist/core/ciGate.d.ts +10 -0
  27. package/dist/core/ciGate.js +21 -0
  28. package/dist/core/ciGate.js.map +1 -0
  29. package/dist/core/dogfood.d.ts +1 -0
  30. package/dist/core/dogfood.js +42 -20
  31. package/dist/core/dogfood.js.map +1 -1
  32. package/dist/core/dogfoodDiscovery.d.ts +8 -0
  33. package/dist/core/dogfoodDiscovery.js +119 -0
  34. package/dist/core/dogfoodDiscovery.js.map +1 -0
  35. package/dist/core/feedback.js +73 -5
  36. package/dist/core/feedback.js.map +1 -1
  37. package/dist/core/fileInspectionReport.js +37 -0
  38. package/dist/core/fileInspectionReport.js.map +1 -1
  39. package/dist/core/intentRouterArchitectureKeywordWeights.d.ts +1 -0
  40. package/dist/core/intentRouterArchitectureKeywordWeights.js +69 -0
  41. package/dist/core/intentRouterArchitectureKeywordWeights.js.map +1 -0
  42. package/dist/core/intentRouterCatalog.js +85 -31
  43. package/dist/core/intentRouterCatalog.js.map +1 -1
  44. package/dist/core/intentRouterDependencyKeywordWeights.d.ts +1 -0
  45. package/dist/core/intentRouterDependencyKeywordWeights.js +100 -0
  46. package/dist/core/intentRouterDependencyKeywordWeights.js.map +1 -0
  47. package/dist/core/intentRouterFileImpactKeywordWeights.d.ts +1 -0
  48. package/dist/core/intentRouterFileImpactKeywordWeights.js +92 -0
  49. package/dist/core/intentRouterFileImpactKeywordWeights.js.map +1 -0
  50. package/dist/core/intentRouterKeywordEarlyGuards.js +8 -3
  51. package/dist/core/intentRouterKeywordEarlyGuards.js.map +1 -1
  52. package/dist/core/intentRouterKeywordSearchGuards.js +28 -24
  53. package/dist/core/intentRouterKeywordSearchGuards.js.map +1 -1
  54. package/dist/core/intentRouterKeywordToolGuards.js +43 -0
  55. package/dist/core/intentRouterKeywordToolGuards.js.map +1 -1
  56. package/dist/core/intentRouterKeywordWeights.js +40 -1222
  57. package/dist/core/intentRouterKeywordWeights.js.map +1 -1
  58. package/dist/core/intentRouterOperationalKeywordWeights.d.ts +1 -0
  59. package/dist/core/intentRouterOperationalKeywordWeights.js +203 -0
  60. package/dist/core/intentRouterOperationalKeywordWeights.js.map +1 -0
  61. package/dist/core/intentRouterPlanningSignals.js +4 -1
  62. package/dist/core/intentRouterPlanningSignals.js.map +1 -1
  63. package/dist/core/intentRouterPrDiffKeywords.d.ts +4 -0
  64. package/dist/core/intentRouterPrDiffKeywords.js +64 -0
  65. package/dist/core/intentRouterPrDiffKeywords.js.map +1 -0
  66. package/dist/core/intentRouterPrDiffSignals.js +6 -0
  67. package/dist/core/intentRouterPrDiffSignals.js.map +1 -1
  68. package/dist/core/intentRouterProductImprovementSignals.d.ts +1 -0
  69. package/dist/core/intentRouterProductImprovementSignals.js +48 -0
  70. package/dist/core/intentRouterProductImprovementSignals.js.map +1 -0
  71. package/dist/core/intentRouterRegressionKeywordMatches.js +3 -0
  72. package/dist/core/intentRouterRegressionKeywordMatches.js.map +1 -1
  73. package/dist/core/intentRouterRegressionKeywordWeights.d.ts +1 -0
  74. package/dist/core/intentRouterRegressionKeywordWeights.js +118 -0
  75. package/dist/core/intentRouterRegressionKeywordWeights.js.map +1 -0
  76. package/dist/core/intentRouterReleaseSignals.d.ts +1 -0
  77. package/dist/core/intentRouterReleaseSignals.js +47 -0
  78. package/dist/core/intentRouterReleaseSignals.js.map +1 -1
  79. package/dist/core/intentRouterReviewSignals.d.ts +1 -0
  80. package/dist/core/intentRouterReviewSignals.js +23 -1
  81. package/dist/core/intentRouterReviewSignals.js.map +1 -1
  82. package/dist/core/intentRouterSearchKeywordWeights.d.ts +1 -0
  83. package/dist/core/intentRouterSearchKeywordWeights.js +407 -0
  84. package/dist/core/intentRouterSearchKeywordWeights.js.map +1 -0
  85. package/dist/core/intentRouterSecurityKeywordWeights.d.ts +1 -0
  86. package/dist/core/intentRouterSecurityKeywordWeights.js +50 -0
  87. package/dist/core/intentRouterSecurityKeywordWeights.js.map +1 -0
  88. package/dist/core/intentRouterTrustFeedbackKeywordWeights.d.ts +1 -0
  89. package/dist/core/intentRouterTrustFeedbackKeywordWeights.js +222 -0
  90. package/dist/core/intentRouterTrustFeedbackKeywordWeights.js.map +1 -0
  91. package/dist/core/intentRouterUnderstandSignals.js +1 -0
  92. package/dist/core/intentRouterUnderstandSignals.js.map +1 -1
  93. package/dist/core/intentRouterWorkSignals.js +3 -0
  94. package/dist/core/intentRouterWorkSignals.js.map +1 -1
  95. package/dist/core/intentRouterWorkflowKeywordWeights.d.ts +1 -0
  96. package/dist/core/intentRouterWorkflowKeywordWeights.js +124 -0
  97. package/dist/core/intentRouterWorkflowKeywordWeights.js.map +1 -0
  98. package/dist/core/issueEngine.js +46 -2
  99. package/dist/core/issueEngine.js.map +1 -1
  100. package/dist/core/memory.d.ts +2 -0
  101. package/dist/core/memory.js +33 -1
  102. package/dist/core/memory.js.map +1 -1
  103. package/dist/core/preflightChangedFiles.d.ts +3 -0
  104. package/dist/core/preflightChangedFiles.js +13 -0
  105. package/dist/core/preflightChangedFiles.js.map +1 -1
  106. package/dist/core/preflightEvidence.d.ts +3 -0
  107. package/dist/core/preflightEvidence.js +3 -0
  108. package/dist/core/preflightEvidence.js.map +1 -1
  109. package/dist/core/privacy.d.ts +2 -0
  110. package/dist/core/privacy.js +10 -0
  111. package/dist/core/privacy.js.map +1 -1
  112. package/dist/core/qualityScorecard.js +25 -13
  113. package/dist/core/qualityScorecard.js.map +1 -1
  114. package/dist/core/startEvidence.js +26 -1
  115. package/dist/core/startEvidence.js.map +1 -1
  116. package/dist/core/startFixedRouteCriteria.js +5 -0
  117. package/dist/core/startFixedRouteCriteria.js.map +1 -1
  118. package/dist/core/startInputs.d.ts +3 -0
  119. package/dist/core/startMissionPolicy.d.ts +1 -1
  120. package/dist/core/startMissionPolicy.js +18 -7
  121. package/dist/core/startMissionPolicy.js.map +1 -1
  122. package/dist/core/startMode.js +17 -4
  123. package/dist/core/startMode.js.map +1 -1
  124. package/dist/core/startReportBuilder.js +1 -1
  125. package/dist/core/startReportBuilder.js.map +1 -1
  126. package/dist/core/startReviewGate.js +26 -4
  127. package/dist/core/startReviewGate.js.map +1 -1
  128. package/dist/core/startRouteActions.js +6 -0
  129. package/dist/core/startRouteActions.js.map +1 -1
  130. package/dist/core/understand.js +60 -13
  131. package/dist/core/understand.js.map +1 -1
  132. package/dist/core/workplan.js +99 -17
  133. package/dist/core/workplan.js.map +1 -1
  134. package/dist/projscan-sbom.cdx.json +6 -6
  135. package/dist/reporters/ciIssueDetails.d.ts +10 -0
  136. package/dist/reporters/ciIssueDetails.js +37 -0
  137. package/dist/reporters/ciIssueDetails.js.map +1 -0
  138. package/dist/reporters/consoleCiReporter.d.ts +2 -1
  139. package/dist/reporters/consoleCiReporter.js +26 -9
  140. package/dist/reporters/consoleCiReporter.js.map +1 -1
  141. package/dist/reporters/consoleFileReporter.js +10 -0
  142. package/dist/reporters/consoleFileReporter.js.map +1 -1
  143. package/dist/reporters/consoleHealthReporter.js +3 -1
  144. package/dist/reporters/consoleHealthReporter.js.map +1 -1
  145. package/dist/reporters/jsonReporter.d.ts +2 -1
  146. package/dist/reporters/jsonReporter.js +17 -10
  147. package/dist/reporters/jsonReporter.js.map +1 -1
  148. package/dist/reporters/markdownFileReporter.js +11 -0
  149. package/dist/reporters/markdownFileReporter.js.map +1 -1
  150. package/dist/reporters/markdownHealthReporter.d.ts +2 -1
  151. package/dist/reporters/markdownHealthReporter.js +5 -5
  152. package/dist/reporters/markdownHealthReporter.js.map +1 -1
  153. package/dist/reporters/scoreBreakdownReporter.d.ts +2 -0
  154. package/dist/reporters/scoreBreakdownReporter.js +24 -0
  155. package/dist/reporters/scoreBreakdownReporter.js.map +1 -0
  156. package/dist/tool-manifest.json +2 -2
  157. package/dist/types/analysis.d.ts +21 -1
  158. package/dist/types/bugHunt.d.ts +3 -0
  159. package/dist/types/config.d.ts +9 -0
  160. package/dist/types/dogfood.d.ts +15 -1
  161. package/dist/types/inspection.d.ts +3 -0
  162. package/dist/types/preflight.d.ts +3 -0
  163. package/dist/types/startMissionControl.d.ts +3 -0
  164. package/dist/types/startMissionReview.d.ts +2 -0
  165. package/dist/utils/ciFailOn.d.ts +5 -0
  166. package/dist/utils/ciFailOn.js +12 -0
  167. package/dist/utils/ciFailOn.js.map +1 -0
  168. package/dist/utils/config.js +3 -1
  169. package/dist/utils/config.js.map +1 -1
  170. package/dist/utils/configBasics.d.ts +2 -0
  171. package/dist/utils/configBasics.js +21 -0
  172. package/dist/utils/configBasics.js.map +1 -1
  173. package/dist/utils/configIssueRules.js +64 -0
  174. package/dist/utils/configIssueRules.js.map +1 -1
  175. package/dist/utils/scoreCalculator.js +77 -16
  176. package/dist/utils/scoreCalculator.js.map +1 -1
  177. package/docs/GUIDE.md +36 -6
  178. package/package.json +1 -1
@@ -1,3 +1,9 @@
1
+ const BASE_SCORE = 100;
2
+ const SEVERITY_WEIGHTS = {
3
+ error: 20,
4
+ warning: 10,
5
+ info: 3,
6
+ };
1
7
  /**
2
8
  * Calculate a project health score (0–100) and letter grade from detected issues.
3
9
  *
@@ -14,23 +20,78 @@
14
20
  * F < 60
15
21
  */
16
22
  export function calculateScore(issues) {
17
- const errors = issues.filter((i) => i.severity === 'error').length;
18
- const warnings = issues.filter((i) => i.severity === 'warning').length;
19
- const infos = issues.filter((i) => i.severity === 'info').length;
20
- const deductions = errors * 20 + warnings * 10 + infos * 3;
21
- const score = Math.max(0, 100 - deductions);
22
- let grade;
23
+ const errors = countSeverity(issues, 'error');
24
+ const warnings = countSeverity(issues, 'warning');
25
+ const infos = countSeverity(issues, 'info');
26
+ const uncappedPenalty = penaltyFor('error', errors) + penaltyFor('warning', warnings) + penaltyFor('info', infos);
27
+ const totalPenalty = Math.min(BASE_SCORE, uncappedPenalty);
28
+ const score = Math.max(0, BASE_SCORE - uncappedPenalty);
29
+ const grade = gradeForScore(score);
30
+ return {
31
+ score,
32
+ grade,
33
+ errors,
34
+ warnings,
35
+ infos,
36
+ scoreBreakdown: buildScoreBreakdown(issues, {
37
+ score,
38
+ grade,
39
+ errors,
40
+ warnings,
41
+ infos,
42
+ totalPenalty,
43
+ uncappedPenalty,
44
+ }),
45
+ };
46
+ }
47
+ function countSeverity(issues, severity) {
48
+ return issues.filter((issue) => issue.severity === severity).length;
49
+ }
50
+ function penaltyFor(severity, count) {
51
+ return count * SEVERITY_WEIGHTS[severity];
52
+ }
53
+ function gradeForScore(score) {
23
54
  if (score >= 90)
24
- grade = 'A';
25
- else if (score >= 80)
26
- grade = 'B';
27
- else if (score >= 70)
28
- grade = 'C';
29
- else if (score >= 60)
30
- grade = 'D';
31
- else
32
- grade = 'F';
33
- return { score, grade, errors, warnings, infos };
55
+ return 'A';
56
+ if (score >= 80)
57
+ return 'B';
58
+ if (score >= 70)
59
+ return 'C';
60
+ if (score >= 60)
61
+ return 'D';
62
+ return 'F';
63
+ }
64
+ function buildScoreBreakdown(issues, score) {
65
+ return {
66
+ baseScore: BASE_SCORE,
67
+ finalScore: score.score,
68
+ grade: score.grade,
69
+ totalPenalty: score.totalPenalty,
70
+ uncappedPenalty: score.uncappedPenalty,
71
+ bySeverity: {
72
+ error: severityBreakdown(score.errors, 'error'),
73
+ warning: severityBreakdown(score.warnings, 'warning'),
74
+ info: severityBreakdown(score.infos, 'info'),
75
+ },
76
+ byCategory: categoryBreakdown(issues),
77
+ };
78
+ }
79
+ function severityBreakdown(count, severity) {
80
+ const weight = SEVERITY_WEIGHTS[severity];
81
+ return { count, weight, penalty: count * weight };
82
+ }
83
+ function categoryBreakdown(issues) {
84
+ const categories = new Map();
85
+ for (const issue of issues) {
86
+ const category = issue.category || 'uncategorized';
87
+ const current = categories.get(category) ?? { count: 0, penalty: 0 };
88
+ current.count += 1;
89
+ current.penalty += SEVERITY_WEIGHTS[issue.severity];
90
+ categories.set(category, current);
91
+ }
92
+ return [...categories.entries()]
93
+ .sort(([a], [b]) => a.localeCompare(b))
94
+ .map(([category, value]) => ({ category, ...value }));
34
95
  }
35
96
  const GRADE_COLORS = {
36
97
  A: 'brightgreen',
@@ -1 +1 @@
1
- {"version":3,"file":"scoreCalculator.js","sourceRoot":"","sources":["../../src/utils/scoreCalculator.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAEjE,MAAM,UAAU,GAAG,MAAM,GAAG,EAAE,GAAG,QAAQ,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,UAAU,CAAC,CAAC;IAE5C,IAAI,KAA2B,CAAC;IAChC,IAAI,KAAK,IAAI,EAAE;QAAE,KAAK,GAAG,GAAG,CAAC;SACxB,IAAI,KAAK,IAAI,EAAE;QAAE,KAAK,GAAG,GAAG,CAAC;SAC7B,IAAI,KAAK,IAAI,EAAE;QAAE,KAAK,GAAG,GAAG,CAAC;SAC7B,IAAI,KAAK,IAAI,EAAE;QAAE,KAAK,GAAG,GAAG,CAAC;;QAC7B,KAAK,GAAG,GAAG,CAAC;IAEjB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,YAAY,GAAyC;IACzD,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,KAAK;CACT,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,KAA2B;IAClD,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,yCAAyC,KAAK,IAAI,KAAK,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAA2B;IACvD,OAAO,uBAAuB,QAAQ,CAAC,KAAK,CAAC,iDAAiD,CAAC;AACjG,CAAC"}
1
+ {"version":3,"file":"scoreCalculator.js","sourceRoot":"","sources":["../../src/utils/scoreCalculator.ts"],"names":[],"mappings":"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"}
package/docs/GUIDE.md CHANGED
@@ -551,13 +551,14 @@ Natural follow-up to `projscan hotspots` - once hotspots tells you _which_ file
551
551
  projscan ci
552
552
  ```
553
553
 
554
- A CI-pipeline-friendly health gate. Runs the full health check and exits with code 1 if the score falls below a threshold. No spinners or banners - clean output for CI logs.
554
+ A CI-pipeline-friendly health gate. Runs the full health check and exits with code 1 if the score falls below a threshold and at least one finding meets the `failOn` severity floor. No spinners or banners - clean output for CI logs.
555
555
 
556
556
  **Options:**
557
557
 
558
558
  | Flag | Description | Default |
559
559
  | ------------------ | ------------------------------------------------ | ----------------------------------------------------------- |
560
560
  | `--min-score <n>` | Minimum passing score (0–100) | `minScore` from `.projscanrc`, else 70 |
561
+ | `--fail-on <severity>` | Lowest severity that can fail a below-threshold gate: `info`, `warning`, or `error` | `failOn` from `.projscanrc`, else `warning` |
561
562
  | `--changed-only` | Gate only on issues in files changed vs base ref | off |
562
563
  | `--base-ref <ref>` | Git base ref for `--changed-only` | auto (origin/main → origin/master → main → master → HEAD~1) |
563
564
 
@@ -566,7 +567,7 @@ A CI-pipeline-friendly health gate. Runs the full health check and exits with co
566
567
  ```bash
567
568
  $ projscan ci --min-score 80
568
569
 
569
- projscan: B (82/100) - 0 errors, 2 warnings, 1 info - PASS (threshold: 80)
570
+ projscan: B (82/100) - 0 errors, 2 warnings, 1 info - PASS (threshold: 80, failOn: warning)
570
571
  ```
571
572
 
572
573
  <img src="npx%20projscan%20ci%20--min-score%2070.gif" alt="npx projscan ci" width="700">
@@ -574,7 +575,8 @@ projscan: B (82/100) - 0 errors, 2 warnings, 1 info - PASS (threshold: 80)
574
575
  **Exit codes:**
575
576
 
576
577
  - `0` - Score meets or exceeds the threshold
577
- - `1` - Score is below the threshold
578
+ - `0` - Score is below the threshold but no finding meets the `failOn` floor
579
+ - `1` - Score is below the threshold and at least one finding meets the `failOn` floor
578
580
 
579
581
  **JSON output** (useful for scripts):
580
582
 
@@ -582,6 +584,10 @@ projscan: B (82/100) - 0 errors, 2 warnings, 1 info - PASS (threshold: 80)
582
584
  projscan ci --min-score 70 --format json
583
585
  ```
584
586
 
587
+ Every `ci.issues[]` item keeps the original issue fields and adds
588
+ annotation-friendly fields: `ruleId`, `message`, primary `location`, all
589
+ `locations`, and `remediation` when a fix hint is available.
590
+
585
591
  **SARIF output** (for GitHub Code Scanning or any SARIF consumer):
586
592
 
587
593
  ```bash
@@ -887,6 +893,9 @@ Use it before broader rollout. The report includes feedback questions for the fi
887
893
  ## Health Score
888
894
 
889
895
  Every `projscan doctor` and `projscan badge` run calculates a health score from 0 to 100 based on detected issues.
896
+ `doctor --format json` and `ci --format json` include `scoreBreakdown` so scripts
897
+ and reviewers can see the base score, per-severity weights, category penalties,
898
+ total penalty, final score, and grade.
890
899
 
891
900
  **Scoring:**
892
901
 
@@ -909,7 +918,8 @@ Every `projscan doctor` and `projscan badge` run calculates a health score from
909
918
  The score appears in all output formats:
910
919
 
911
920
  - **Console**: Shown at the top of the doctor report
912
- - **JSON**: Included as `health.score` and `health.grade` fields
921
+ - **JSON**: Included as `health.score`, `health.grade`, and `health.scoreBreakdown`
922
+ fields. CI uses the same structure under `ci.scoreBreakdown`.
913
923
  - **Markdown**: Shown as a heading with an auto-generated shields.io badge
914
924
  - **HTML**: Shown in the health summary card
915
925
  - **SARIF**: Not surfaced directly - SARIF is per-issue, not per-project. The score still drives `ci`'s exit code.
@@ -1005,6 +1015,7 @@ ProjScan loads a project-wide config from one of:
1005
1015
  ```json
1006
1016
  {
1007
1017
  "minScore": 80,
1018
+ "failOn": "warning",
1008
1019
  "baseRef": "origin/main",
1009
1020
  "ignore": ["**/fixtures/**", "**/generated/**"],
1010
1021
  "scan": {
@@ -1013,6 +1024,9 @@ ProjScan loads a project-wide config from one of:
1013
1024
  "offline": false
1014
1025
  },
1015
1026
  "disableRules": ["missing-editorconfig", "large-*"],
1027
+ "suppress": {
1028
+ "hardcoded-secret": ["src/firebase.ts"]
1029
+ },
1016
1030
  "severityOverrides": {
1017
1031
  "missing-prettier": "info"
1018
1032
  },
@@ -1034,12 +1048,14 @@ ProjScan loads a project-wide config from one of:
1034
1048
  | Field | Type | Effect |
1035
1049
  | --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1036
1050
  | `minScore` | number (0–100) | Default threshold for `projscan ci`. Clamped to 0–100. |
1051
+ | `failOn` | `'info' \| 'warning' \| 'error'` | Lowest severity that can fail a below-threshold `projscan ci` gate. Default `warning`; set `info` for legacy strictness or `error` for error-only blocking. |
1037
1052
  | `baseRef` | string | Default base ref for `--changed-only`. |
1038
1053
  | `ignore` | string[] | Extra glob patterns added to the built-in ignore list (`node_modules`, `.git`, `dist`, `build`, `coverage`, `.next`, `.nuxt`, `.cache`, `.turbo`, `.output`). |
1039
1054
  | `scan.includeIgnored` | boolean | Explicitly include files hidden by Git ignore rules. Default `false`. |
1040
1055
  | `scan.scanEnvValues` | boolean | Explicitly read `.env*` contents during secret-pattern checks. Default `false`; `.env` files are path-only. |
1041
1056
  | `scan.offline` | boolean | Block projscan network-capable features: telemetry sending, `audit`, registry checks, and optional semantic model loading. Default `false`. |
1042
1057
  | `disableRules` | string[] | Silence rules by id. Exact match (`missing-prettier`) or wildcard prefix (`large-*`). |
1058
+ | `suppress` | `Record<string, string[]>` | Silence a rule only for matching paths/globs, for example `{ "hardcoded-secret": ["src/firebase.ts"] }`. Other rules still run on that file. |
1043
1059
  | `severityOverrides` | `Record<string, 'info' \| 'warning' \| 'error'>` | Remap a rule's severity. Useful for downgrading project-specific false positives without disabling them. |
1044
1060
  | `reportPolicies` | `Record<string, { reportScope?: string[]; redactPaths?: boolean }>` | Named evidence export presets selected with `--report-policy <name>` on `analyze`, `doctor`, and `ci`. |
1045
1061
  | `hotspots.limit` | number (1–100) | Default limit for `projscan hotspots`. |
@@ -1047,6 +1063,12 @@ ProjScan loads a project-wide config from one of:
1047
1063
 
1048
1064
  Invalid JSON in a discovered config file is a hard error - projscan exits rather than silently ignoring it.
1049
1065
 
1066
+ Use inline suppressions for a single confirmed false positive:
1067
+
1068
+ ```ts
1069
+ const firebaseKey = "AIza..." // projscan-ignore-line hardcoded-secret -- Firebase web keys are public identifiers
1070
+ ```
1071
+
1050
1072
  ### Embedded config in `package.json`
1051
1073
 
1052
1074
  If you prefer to keep everything in `package.json`:
@@ -1564,8 +1586,10 @@ If you'd rather skip Code Scanning, `projscan init github-action` writes a pull-
1564
1586
  The `ci` command is purpose-built for pipelines:
1565
1587
 
1566
1588
  ```bash
1567
- projscan ci # Fail if score < 70 (default)
1589
+ projscan ci # Fail if score < 70 and warning/error findings exist
1568
1590
  projscan ci --min-score 80 # Custom threshold
1591
+ projscan ci --fail-on info # Legacy strictness: info can fail the gate
1592
+ projscan ci --fail-on error # Only errors can fail a below-threshold gate
1569
1593
  projscan ci --changed-only # Gate only on PR diff
1570
1594
  projscan ci --format json # JSON output for scripts
1571
1595
  projscan ci --format sarif > projscan.sarif # SARIF for any consumer
@@ -1577,9 +1601,15 @@ projscan ci --format sarif > projscan.sarif # SARIF for any consumer
1577
1601
  result=$(projscan ci --min-score 0 --format json)
1578
1602
  pass=$(echo "$result" | jq '.ci.pass')
1579
1603
  score=$(echo "$result" | jq '.ci.score')
1580
- echo "Score: $score, Pass: $pass"
1604
+ fail_on=$(echo "$result" | jq -r '.ci.failOn')
1605
+ echo "Score: $score, Pass: $pass, FailOn: $fail_on"
1581
1606
  ```
1582
1607
 
1608
+ For PR annotation tooling, read `.ci.issues[]`. Each issue includes `ruleId`,
1609
+ `severity`, `message`, primary `location`, all `locations`, and `remediation`
1610
+ when available. Gate metadata lives at `.ci.failOn`, `.ci.scorePass`, and
1611
+ `.ci.severityFloorMet`.
1612
+
1583
1613
  ### Tracking health over time in CI
1584
1614
 
1585
1615
  Combine `ci` with `diff` to track regressions:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "projscan",
3
3
  "mcpName": "io.github.abhiyoheswaran1/projscan",
4
- "version": "4.9.3",
4
+ "version": "4.10.0",
5
5
  "description": "Local code intelligence for agent-assisted engineering. Focused daily workflows for repo orientation before edits, proof before handoff or commit, and release-candidate review, with AST-backed evidence through an MCP server and CLI. Runs locally by default.",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",