pqcheck 0.16.17 → 0.16.19
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 +1 -1
- package/bin/cipherwake-statusline.js +46 -0
- package/bin/pqcheck.js +64 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/pqcheck)
|
|
9
9
|
[](./LICENSE)
|
|
10
10
|
|
|
11
|
-
> **Latest: v0.16.
|
|
11
|
+
> **Latest: v0.16.19** — `pqcheck setup` now COMPOSES with an existing Claude Code `statusLine.command` (e.g., PinnedAI's) via a new `--prepend=<cmd>` flag on `cipherwake-statusline`. Both tools' badges render in the single statusLine slot. Previously, Cipherwake politely skipped if any prior statusLine existed — which meant you'd only ever see one tool's badge. Now: composed wrap with 5s timeout + graceful degradation on prepend failure + idempotent re-runs. [Full changelog →](./CHANGELOG.md)
|
|
12
12
|
|
|
13
13
|
## Two ways to use it
|
|
14
14
|
|
|
@@ -21,10 +21,56 @@
|
|
|
21
21
|
import { readFileSync, existsSync } from "node:fs";
|
|
22
22
|
import { join, dirname } from "node:path";
|
|
23
23
|
import { homedir } from "node:os";
|
|
24
|
+
import { execSync } from "node:child_process";
|
|
24
25
|
|
|
25
26
|
const GLOBAL_STATE_FILE = join(homedir(), ".config", "cipherwake", "last-scan.json");
|
|
26
27
|
const STALE_THRESHOLD_HOURS = 24;
|
|
27
28
|
|
|
29
|
+
// v0.16.19 — `--prepend=<command>` flag for statusLine composition. Claude
|
|
30
|
+
// Code's `statusLine.command` only supports ONE command, which means two
|
|
31
|
+
// tools (e.g. Cipherwake + PinnedAI) both writing their own statusLine
|
|
32
|
+
// silently clobber each other depending on install order. When a customer
|
|
33
|
+
// runs both, they only see whichever installed last. With --prepend, the
|
|
34
|
+
// Cipherwake binary runs the other tool's command first, joins its output
|
|
35
|
+
// with a separator, and renders Cipherwake's own line after. Result:
|
|
36
|
+
// `[pinned output] · ◆ Cipherwake · domain ✓ PASS · 5m ago` — both
|
|
37
|
+
// surfaces visible in the single statusLine slot.
|
|
38
|
+
//
|
|
39
|
+
// pqcheck setup detects an existing statusLine.command at install time
|
|
40
|
+
// and writes the wrapper form automatically:
|
|
41
|
+
// "command": "npx --package=pqcheck@latest cipherwake-statusline --prepend='<prior-command>'"
|
|
42
|
+
//
|
|
43
|
+
// If the prior command errors (or the tool was uninstalled), this binary
|
|
44
|
+
// swallows the failure and just renders Cipherwake's part — never causes
|
|
45
|
+
// the whole statusLine to break.
|
|
46
|
+
const PREPEND_SEPARATOR = " · ";
|
|
47
|
+
const prependArg = process.argv.find((a) => a.startsWith("--prepend="));
|
|
48
|
+
if (prependArg) {
|
|
49
|
+
const prependCmd = prependArg.slice("--prepend=".length);
|
|
50
|
+
if (prependCmd) {
|
|
51
|
+
try {
|
|
52
|
+
// 5s soft timeout — statusLine rendering must stay snappy. If the
|
|
53
|
+
// other tool hangs, we cut it off and continue with Cipherwake's part.
|
|
54
|
+
// stdio: ignore on stderr so a noisy prior tool doesn't pollute the
|
|
55
|
+
// bar; we want only its rendered stdout output.
|
|
56
|
+
const out = execSync(prependCmd, {
|
|
57
|
+
encoding: "utf8",
|
|
58
|
+
timeout: 5_000,
|
|
59
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
60
|
+
env: process.env,
|
|
61
|
+
}).trim();
|
|
62
|
+
if (out) {
|
|
63
|
+
process.stdout.write(out);
|
|
64
|
+
process.stdout.write(PREPEND_SEPARATOR);
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// Prepend command failed (uninstalled, timeout, errored). Skip
|
|
68
|
+
// silently — never break the whole statusLine because of a
|
|
69
|
+
// composition partner.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
28
74
|
// v0.16.6 — project-aware state lookup. Walk up from CWD looking for a
|
|
29
75
|
// repo-local `.cipherwake/last-scan.json`. This way each project shows
|
|
30
76
|
// its own last scan, and switching projects doesn't bleed the previous
|
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
|
-
|
|
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
|
|
@@ -6297,19 +6317,54 @@ async function runSetupCommand(args) {
|
|
|
6297
6317
|
settings = JSON.parse(raw);
|
|
6298
6318
|
existed = true;
|
|
6299
6319
|
} catch { /* will create */ }
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6320
|
+
// v0.16.19 — statusLine composition. Previous behavior was: if any
|
|
6321
|
+
// statusLine.command already existed, SKIP. That was safe (never
|
|
6322
|
+
// clobbered other tools) but invisible — a user with PinnedAI's
|
|
6323
|
+
// statusline installed would never see Cipherwake's after running
|
|
6324
|
+
// `pqcheck setup`. Now: detect the prior command and wrap it via
|
|
6325
|
+
// `cipherwake-statusline --prepend=<prior>` so both render in the
|
|
6326
|
+
// single statusLine slot. The prepend logic in cipherwake-statusline.js
|
|
6327
|
+
// swallows prepend-command failures silently, so this composition
|
|
6328
|
+
// survives the user later uninstalling the partner tool.
|
|
6329
|
+
//
|
|
6330
|
+
// Skip the wrap when the existing command IS already our wrapper
|
|
6331
|
+
// (idempotent re-install — don't recursively nest).
|
|
6332
|
+
const CIPHERWAKE_CMD_PREFIX = "npx --package=pqcheck@latest cipherwake-statusline";
|
|
6333
|
+
const priorCmd = (existed && settings.statusLine && typeof settings.statusLine === "object")
|
|
6334
|
+
? String(settings.statusLine.command || "").trim()
|
|
6335
|
+
: "";
|
|
6336
|
+
const priorIsOurs = priorCmd.startsWith("npx --package=pqcheck") && priorCmd.includes("cipherwake-statusline");
|
|
6337
|
+
|
|
6338
|
+
if (priorIsOurs) {
|
|
6339
|
+
console.log(color("dim", ` ⊝ ${displayPath} statusLine already points at cipherwake-statusline — leaving alone`));
|
|
6340
|
+
installSummary.push({ component: "Claude Code statusLine", path: settingsPath, status: "skipped-already-ours" });
|
|
6305
6341
|
} else {
|
|
6306
6342
|
const backupPath = existed ? await backupSettingsJson(settingsPath) : null;
|
|
6307
6343
|
if (backupPath) console.log(color("dim", ` backup: ${backupPath}`));
|
|
6308
|
-
|
|
6344
|
+
let command;
|
|
6345
|
+
if (priorCmd) {
|
|
6346
|
+
// Compose: wrap the existing command via --prepend.
|
|
6347
|
+
// Single-quote the prior command + escape any single quotes
|
|
6348
|
+
// (rare in practice but defensive).
|
|
6349
|
+
const safe = priorCmd.replace(/'/g, `'\\''`);
|
|
6350
|
+
command = `${CIPHERWAKE_CMD_PREFIX} --prepend='${safe}'`;
|
|
6351
|
+
console.log(color("green", ` ✓ composed statusLine: existing command wrapped via --prepend → ${displayPath}`));
|
|
6352
|
+
console.log(color("dim", ` prior command: ${priorCmd.slice(0, 80)}${priorCmd.length > 80 ? "..." : ""}`));
|
|
6353
|
+
console.log(color("dim", ` both surfaces now render in the single statusLine slot`));
|
|
6354
|
+
} else {
|
|
6355
|
+
// Fresh install — no prior command to compose with.
|
|
6356
|
+
command = CIPHERWAKE_CMD_PREFIX;
|
|
6357
|
+
console.log(color("green", ` ✓ added statusLine config → ${displayPath}`));
|
|
6358
|
+
}
|
|
6359
|
+
settings.statusLine = { type: "command", command };
|
|
6309
6360
|
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
6310
6361
|
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
6311
|
-
|
|
6312
|
-
|
|
6362
|
+
installSummary.push({
|
|
6363
|
+
component: "Claude Code statusLine",
|
|
6364
|
+
path: settingsPath,
|
|
6365
|
+
status: existed && priorCmd ? "installed-composed" : (existed ? "installed-updated" : "installed-created"),
|
|
6366
|
+
backup: backupPath,
|
|
6367
|
+
});
|
|
6313
6368
|
}
|
|
6314
6369
|
} catch (err) {
|
|
6315
6370
|
console.log(color("red", ` ✗ statusLine config install failed: ${err.message}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pqcheck",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.19",
|
|
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",
|