@fullstackhouse/cerber 0.31.0 → 0.31.1

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/dist/cli/index.js CHANGED
@@ -29,8 +29,8 @@ const { version } = createRequire(import.meta.url)("../../package.json");
29
29
  * poll. `allowDegraded` is for `serve --no-poll`, which is still useful for
30
30
  * reading artifacts already on disk.
31
31
  */
32
- async function requireTools(allowDegraded = false) {
33
- const checks = await preflight();
32
+ async function requireTools(allowDegraded = false, opts = {}) {
33
+ const checks = await preflight(opts);
34
34
  if (!checks.some(isBlocking))
35
35
  return;
36
36
  const message = formatBlockers(checks);
@@ -58,7 +58,8 @@ program
58
58
  .option("-t, --trust", "let the review run commands in the checkout (tests, typecheck, git log) whatever the trust config says")
59
59
  .option("--no-trust", "keep the review read-only even if a trust rule matches")
60
60
  .action(async (prs, opts) => {
61
- await requireTools();
61
+ // `--no-source` never clones the PR head, so a missing `git` is not its problem.
62
+ await requireTools(false, { git: opts.source });
62
63
  let refs;
63
64
  if (opts.awaitingMe) {
64
65
  const found = await searchAwaitingMe(opts.repo);
@@ -384,6 +385,8 @@ program
384
385
  for (const c of checks) {
385
386
  if (c.status === "ok") {
386
387
  console.log(` ✔ ${c.name.padEnd(22)} ${c.detail ?? ""}`.trimEnd());
388
+ if (c.note)
389
+ console.log(` ${c.note}`);
387
390
  }
388
391
  else {
389
392
  console.log(` ✗ ${c.name.padEnd(22)} ${c.status === "missing" ? "not installed" : "not logged in"}`);
@@ -431,7 +434,7 @@ program
431
434
  }
432
435
  // Without `gh` the cockpit binds, polls into an error loop and shows an
433
436
  // empty queue; `--no-poll` is the one shape that still works offline.
434
- await requireTools(!opts.poll);
437
+ await requireTools(!opts.poll, { git: opts.source });
435
438
  const threshold = Math.min(100, Math.max(50, Number(opts.autoSendThreshold) || 90));
436
439
  if (opts.autoSend) {
437
440
  console.log(`⚠ auto-send is ON: APPROVE verdicts with confidence ≥ ${threshold}% will be sent to GitHub as you, without a per-review confirmation.\n` +
@@ -54,7 +54,17 @@ export async function checkClaude() {
54
54
  fix: "Run `claude` once and log in — a review is drafted through your Claude Code session.",
55
55
  };
56
56
  }
57
- return { name: "claude (Claude Code)", status: "ok", detail: version.out.split("\n")[0] };
57
+ // `claude --version` answers whether or not anyone is logged in, and the only
58
+ // honest way to test the session is to spend a turn on it — too expensive for
59
+ // something that runs before every review. So the check says what it knows
60
+ // and names where the real answer shows up, rather than implying it verified
61
+ // a login it never looked at.
62
+ return {
63
+ name: "claude (Claude Code)",
64
+ status: "ok",
65
+ detail: version.out.split("\n")[0],
66
+ note: "installed; login not checked — a logged-out CLI fails on the first review, with claude's own message",
67
+ };
58
68
  }
59
69
  export async function checkGit() {
60
70
  const version = await run("git", ["--version"]);
@@ -67,9 +77,16 @@ export async function checkGit() {
67
77
  }
68
78
  return { name: "git", status: "ok", detail: version.out.split("\n")[0] };
69
79
  }
70
- /** Everything cerber shells out to, checked in parallel. */
71
- export async function preflight() {
72
- return Promise.all([checkGh(), checkClaude(), checkGit()]);
80
+ /**
81
+ * Everything cerber shells out to for this command, checked in parallel.
82
+ *
83
+ * `git` is only reached when a run clones the PR head, so `--no-source` must
84
+ * not be blocked by its absence: that mode reviews the diff alone and never
85
+ * touches a checkout.
86
+ */
87
+ export async function preflight(opts = {}) {
88
+ const needsGit = opts.git ?? true;
89
+ return Promise.all([checkGh(), checkClaude(), ...(needsGit ? [checkGit()] : [])]);
73
90
  }
74
91
  /** The blocking checks, formatted as the message printed instead of failing later. */
75
92
  export function formatBlockers(checks) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fullstackhouse/cerber",
3
- "version": "0.31.0",
3
+ "version": "0.31.1",
4
4
  "description": "AI code-review cockpit: Claude reviews your PRs into local artifacts; nothing reaches GitHub until you say so.",
5
5
  "license": "MIT",
6
6
  "keywords": [