pqcheck 0.16.16 → 0.16.18

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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![npm downloads](https://img.shields.io/npm/dm/pqcheck.svg?style=flat-square&color=06b6d4)](https://www.npmjs.com/package/pqcheck)
9
9
  [![license](https://img.shields.io/npm/l/pqcheck.svg?style=flat-square&color=06b6d4)](./LICENSE)
10
10
 
11
- > **Latest: v0.16.16** — `pqcheck setup` now defaults to per-project install of the Claude Code statusLine + hooks (`./.claude/settings.json` instead of `~/.claude/settings.json`). The Cipherwake badge will only fire in projects where you ran setup, not across every Claude Code session on the machine. Pass `--scope global` to opt back into machine-wide install. [Full changelog →](./CHANGELOG.md)
11
+ > **Latest: v0.16.18** — `--version` now reads dynamically from `package.json` instead of a hardcoded constant. Prior releases (0.16.16, 0.16.17) shipped with the constant left at `0.16.15`, so AI agents citing "I ran pqcheck X.Y.Z" were citing the wrong version. Single source of truth from here on; locked by a unit test against future drift. [Full changelog →](./CHANGELOG.md)
12
12
 
13
13
  ## Two ways to use it
14
14
 
package/bin/pqcheck.js CHANGED
@@ -24,7 +24,27 @@
24
24
  })();
25
25
 
26
26
  const API_BASE = process.env.PQCHECK_API_BASE || "https://cipherwake.io";
27
- const VERSION = "0.16.15";
27
+
28
+ // v0.16.18 — read VERSION dynamically from package.json so it can never
29
+ // drift from the published npm version again. Prior to this, the constant
30
+ // was hardcoded and required a manual edit on every release — and the
31
+ // 0.16.16/0.16.17 publishes shipped with VERSION="0.16.15" because the
32
+ // hardcode bump was forgotten. AI agents reporting "I ran pqcheck X.Y.Z"
33
+ // were citing the wrong version. Now: single source of truth = package.json.
34
+ // Uses createRequire so the JSON load is synchronous; ~1ms at startup,
35
+ // negligible vs first network call. Falls back to "unknown" on file-read
36
+ // failure (should never happen in production install layout).
37
+ const VERSION = await (async () => {
38
+ try {
39
+ const { readFileSync } = await import("node:fs");
40
+ const { join, dirname } = await import("node:path");
41
+ const { fileURLToPath } = await import("node:url");
42
+ const here = dirname(fileURLToPath(import.meta.url));
43
+ return JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8")).version || "unknown";
44
+ } catch {
45
+ return "unknown";
46
+ }
47
+ })();
28
48
 
29
49
  // v0.16.15 — attribution suffix. When the CLI runs inside GitHub Actions
30
50
  // (GH sets GITHUB_ACTIONS=true automatically in every step) we append
@@ -3051,12 +3071,51 @@ async function runScanBasedDeployCheck(domain, args) {
3051
3071
  try {
3052
3072
  resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}`, { headers });
3053
3073
  } catch (err) {
3074
+ // v0.16.17 — the v0.16.13 fail-loud AI guard fix was applied to the main
3075
+ // trust-diff deploy-check path but missed THIS fallback path (no-baseline
3076
+ // first-deploy), so a network blip on the very first `pqcheck deploy-check
3077
+ // <new-domain> --ai` exited 3 with no CIPHERWAKE_AI_GUARD_RESULT block.
3078
+ // The calling AI agent then had no ship_decision to route on and could
3079
+ // silently continue shipping — exactly the failure mode the protocol
3080
+ // exists to prevent. Same emit-block-and-exit pattern as trust-diff.
3054
3081
  console.error(color("red", `error: network failure calling /api/scan: ${err.message}`));
3055
- process.exit(3);
3082
+ return emitAiGuardReviewAndExit(args, {
3083
+ code: "deploy_check_fetch_failed",
3084
+ message: `Network failure calling /api/scan: ${err?.message || "fetch failed"}`,
3085
+ exitCode: 3,
3086
+ });
3056
3087
  }
3057
3088
  if (!resp.ok) {
3058
- console.error(color("red", `error: /api/scan returned ${resp.status}`));
3059
- process.exit(3);
3089
+ // v0.16.17 same fix as above for the HTTP-non-OK path. The 429 case
3090
+ // is especially load-bearing: a brand new AI-coder workflow trying its
3091
+ // first deploy-check on a fresh project will commonly hit per-IP rate
3092
+ // limits, get a bare `error: /api/scan returned 429`, and exit 3 with no
3093
+ // guard block. The AI agent then has nothing to parse. Emitting a
3094
+ // ship_decision=review block with a quota-specific error code lets the
3095
+ // agent surface the rate-limit problem to the user instead of silently
3096
+ // continuing. Surface the body message + auth hint when available so
3097
+ // the user knows whether to wait or get an API key.
3098
+ const body = await safeJSON(resp);
3099
+ const statusLabel = resp.status === 429
3100
+ ? "rate-limited"
3101
+ : resp.status === 401 || resp.status === 403
3102
+ ? "auth failed"
3103
+ : `${resp.status}`;
3104
+ console.error(color("red", `error: /api/scan returned ${resp.status} (${statusLabel})`));
3105
+ if (body?.message) console.error(color("dim", body.message));
3106
+ if (body?.hint) console.error(color("dim", body.hint));
3107
+ if (resp.status === 429) {
3108
+ console.error(color("dim", "Higher quota via free API key (no card): https://cipherwake.io/account#api-keys"));
3109
+ }
3110
+ const code = resp.status === 429
3111
+ ? "deploy_check_rate_limited"
3112
+ : resp.status === 401 || resp.status === 403
3113
+ ? "deploy_check_auth_failed"
3114
+ : "deploy_check_scan_failed";
3115
+ const message = resp.status === 429
3116
+ ? (body?.message || "Per-IP rate limit hit on /api/scan. Wait ~1 minute or use an API key for higher quota.")
3117
+ : body?.message || `Cipherwake /api/scan returned ${resp.status}.`;
3118
+ return emitAiGuardReviewAndExit(args, { code, message, exitCode: 3 });
3060
3119
  }
3061
3120
  const report = await resp.json();
3062
3121
  const findings = Array.isArray(report.findings) ? report.findings : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pqcheck",
3
- "version": "0.16.16",
3
+ "version": "0.16.18",
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",