pqcheck 0.16.0 → 0.16.2

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.
@@ -121,6 +121,18 @@ const sevSegment = (!isUnreachable && max_severity && max_severity !== "none")
121
121
  ? ` · ${String(max_severity).toUpperCase()}`
122
122
  : "";
123
123
 
124
+ // v0.16.2 — drift narrative suffix. "stable 14d" / "drifted 2d ago" / "now".
125
+ // Sourced from state.lastChanged (smartCache populates) so the customer
126
+ // sees time-series anchor at a glance, not just a single-point snapshot.
127
+ let stabilitySegment = "";
128
+ if (!isUnreachable && state.lastChanged) {
129
+ const dms = Date.now() - new Date(state.lastChanged).getTime();
130
+ const days = Math.floor(dms / 86400000);
131
+ if (days <= 0) stabilitySegment = " · drifted today";
132
+ else if (days < 7) stabilitySegment = ` · drifted ${days}d ago`;
133
+ else stabilitySegment = ` · stable ${days}d`;
134
+ }
135
+
124
136
  process.stdout.write(
125
137
  c(cdec, "◆") +
126
138
  " " +
package/bin/pqcheck.js CHANGED
@@ -24,7 +24,7 @@
24
24
  })();
25
25
 
26
26
  const API_BASE = process.env.PQCHECK_API_BASE || "https://cipherwake.io";
27
- const VERSION = "0.16.0";
27
+ const VERSION = "0.16.2";
28
28
 
29
29
  // API-key support — paid tiers (Starter $29 / Growth $79 / Scale $199) get
30
30
  // per-account monthly quotas instead of the per-IP rate limit. Set via:
@@ -831,11 +831,13 @@ function countVerifiedSignals(report) {
831
831
  if (report?.sourceMaps?.reachable) cats.push("source maps");
832
832
  // 6. TLS posture
833
833
  if (report?.publicSurface?.tlsVersion || report?.tlsVersion) cats.push("TLS");
834
- // 7. Mixed content — not yet a separate probe but counted when we have
835
- // publicDeps (which exposes any loadedOverHttps:false items).
836
- if (report?.publicDeps?.fetched) cats.push("mixed-content");
834
+ // 7. Mixed content — v0.16.2 promoted to dedicated probe; falls back to
835
+ // publicDeps inference when explicit field missing.
836
+ if (report?.mixedContent?.probed || report?.publicDeps?.fetched) cats.push("mixed-content");
837
837
  // 8. Subdomain scale (from CT log scan)
838
838
  if (typeof report?.publicSurface?.subdomainCount === "number") cats.push("subdomain scale");
839
+ // 9. Protected paths (v0.16.2 — the headline feature)
840
+ if (report?.protectedPaths?.probed) cats.push("protected paths");
839
841
  return { count: cats.length, categories: cats };
840
842
  }
841
843
 
@@ -2762,19 +2764,39 @@ async function runScanBasedDeployCheck(domain, args) {
2762
2764
  ];
2763
2765
  }
2764
2766
 
2767
+ // v0.16.0 high-yield rendering — same shape as runOneScan but with the
2768
+ // "first-deploy" context line above the brand header (since this path
2769
+ // fires only when there's no baseline to diff against).
2770
+ const verbose = isVerboseMode(args);
2771
+ const highSevFindings = findings.filter((f) => severityRank(f.severity) >= 3);
2772
+ const alertCount = highSevFindings.length;
2773
+
2765
2774
  console.log("");
2766
2775
  console.log(color("dim", ` ℹ first deploy-check for ${domain} — using current scan state (no baseline yet to diff against)`));
2767
2776
  console.log("");
2768
- console.log(formatAiBanner({
2769
- domain,
2770
- kind: "scan",
2771
- dbr: report.score,
2772
- grade: report.grade,
2773
- maxSeverity: maxSev,
2774
- shipDecision,
2775
- unreachable,
2776
- }));
2777
- console.log(formatAiBody({ topIssue, whyMatters, nextActions }));
2777
+ console.log(formatBrandHeader(domain));
2778
+ console.log("");
2779
+ console.log(formatDecisionPulse({ shipDecision, alertCount, unreachable, diffContext: true }));
2780
+
2781
+ if (unreachable) {
2782
+ console.log(formatAiBody({ topIssue, whyMatters, nextActions }));
2783
+ } else {
2784
+ if (alertCount > 0) {
2785
+ const alerts = formatAlertsLine(highSevFindings);
2786
+ if (alerts) console.log(alerts);
2787
+ }
2788
+ const tpl = formatTrustPostureLine(report);
2789
+ if (tpl) console.log(tpl);
2790
+ const vsl = formatVerifiedSignalsLine(report);
2791
+ if (vsl) console.log(vsl);
2792
+ if (verbose) {
2793
+ console.log("");
2794
+ console.log(formatHighYieldVerbose(report));
2795
+ } else {
2796
+ console.log(color("dim", " Run with --verbose to see all verified signals."));
2797
+ }
2798
+ }
2799
+
2778
2800
  console.log(formatAiFooterBlock({
2779
2801
  status: shipDecision,
2780
2802
  domain,
@@ -2910,16 +2932,41 @@ async function runTrustDiffCommand(args) {
2910
2932
  `If not intentional: revert the deploy or investigate the drift source.`,
2911
2933
  ];
2912
2934
 
2913
- console.log("");
2914
- console.log(formatAiBanner({
2935
+ // v0.16.0 high-yield rendering for trust-diff.
2936
+ const verbose = isVerboseMode(args);
2937
+ const diffNoChange = deltas.length === 0;
2938
+ const fakeReport = {
2915
2939
  domain,
2916
- kind: "trust-diff",
2917
- dbr: result.current_score,
2940
+ score: result.current_score,
2918
2941
  grade: result.current_grade,
2919
- maxSeverity: maxSev,
2920
- shipDecision,
2921
- }));
2922
- console.log(formatAiBody({ topIssue, whyMatters, nextActions }));
2942
+ _meta: { lastChanged: result.last_changed },
2943
+ sectorRanking: result.sectorRanking,
2944
+ };
2945
+
2946
+ console.log("");
2947
+ console.log(formatBrandHeader(domain));
2948
+ console.log("");
2949
+ console.log(formatDecisionPulse({ shipDecision, alertCount: deltas.length, unreachable: false, diffContext: true }));
2950
+
2951
+ if (deltas.length > 0) {
2952
+ // Surface top deltas as alerts
2953
+ const alertFindings = deltas.slice(0, 3).map((d) => ({
2954
+ severity: d.severity || "medium",
2955
+ title: d.title || d.what_changed || d.type || "drift",
2956
+ id: d.id,
2957
+ }));
2958
+ const alerts = formatAlertsLine(alertFindings);
2959
+ if (alerts) console.log(alerts);
2960
+ if (deltas.length > 3) console.log(color("dim", ` +${deltas.length - 3} more (run with --verbose)`));
2961
+ }
2962
+ const tpl = formatTrustPostureLine(fakeReport);
2963
+ if (tpl) console.log(tpl);
2964
+ const vsl = formatVerifiedSignalsLine(fakeReport, { diffNoChange });
2965
+ if (vsl) console.log(vsl);
2966
+ if (!verbose) {
2967
+ console.log(color("dim", " Run with --verbose to see all verified signals."));
2968
+ }
2969
+
2923
2970
  console.log(formatAiFooterBlock({
2924
2971
  status: shipDecision,
2925
2972
  domain,
@@ -3123,16 +3170,46 @@ async function runPreviewDiffCommand(args) {
3123
3170
  `Each "- CSP weakened" or "~ HSTS weakened" reduces production safety.`,
3124
3171
  ];
3125
3172
 
3126
- console.log("");
3127
- console.log(formatAiBanner({
3128
- domain: result?.production?.domain || productionUrl,
3129
- kind: "preview-diff",
3130
- dbr: prevScore,
3173
+ // v0.16.2 high-yield rendering for preview-diff.
3174
+ const verbose = isVerboseMode(args);
3175
+ const diffNoChange = !hasUnexpectedDiff;
3176
+ const headerDomain = result?.production?.domain || productionUrl;
3177
+ const fakeReport = {
3178
+ domain: headerDomain,
3179
+ score: prevScore,
3131
3180
  grade: result?.preview?.grade,
3132
- maxSeverity: maxSev,
3133
- shipDecision,
3134
- }));
3135
- console.log(formatAiBody({ topIssue, whyMatters, nextActions }));
3181
+ _meta: { lastChanged: result?.production?.lastChanged },
3182
+ sectorRanking: result?.production?.sectorRanking,
3183
+ };
3184
+ // Render top deltas as alert lines (1 per finding, max 3)
3185
+ const deltaLines = summaryLines.filter((l) => !/no meaningful/i.test(l));
3186
+
3187
+ console.log("");
3188
+ console.log(formatBrandHeader(headerDomain));
3189
+ console.log("");
3190
+ console.log(formatDecisionPulse({ shipDecision, alertCount: deltaLines.length, unreachable: false, diffContext: true }));
3191
+
3192
+ if (deltaLines.length > 0) {
3193
+ for (const dl of deltaLines.slice(0, 3)) {
3194
+ const sym = dl.startsWith("⛔") || dl.startsWith("-") ? "red" : dl.startsWith("⚠") || dl.startsWith("~") ? "yellow" : "dim";
3195
+ console.log(color(sym, dl));
3196
+ }
3197
+ if (deltaLines.length > 3) console.log(color("dim", ` +${deltaLines.length - 3} more (run with --verbose)`));
3198
+ }
3199
+ const tpl = formatTrustPostureLine(fakeReport);
3200
+ if (tpl) console.log(tpl);
3201
+ const vsl = formatVerifiedSignalsLine(fakeReport, { diffNoChange });
3202
+ if (vsl) console.log(vsl);
3203
+ if (!verbose) {
3204
+ console.log(color("dim", " Run with --verbose to see all verified signals."));
3205
+ }
3206
+
3207
+ // (Old banner + body removed in v0.16.2; new high-yield layout above
3208
+ // replaces them. AI footer still lands below for downstream agents.)
3209
+ if (verbose) {
3210
+ console.log("");
3211
+ console.log(formatAiBody({ topIssue, whyMatters, nextActions }));
3212
+ }
3136
3213
  console.log(formatAiFooterBlock({
3137
3214
  status: shipDecision,
3138
3215
  domain: result?.production?.domain || productionUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pqcheck",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "Deploy gate for AI-coded web apps. `pqcheck deploy-check --ai` returns ship_decision=pass|review|block for Claude Code / Cursor / Copilot / Aider to gate deploys before they ship. Anonymous, no signup, free for first use.",
5
5
  "keywords": [
6
6
  "ai-coder",