claude-plan-review 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -60,15 +60,6 @@ npx claude-plan-review init --published # if you have Node
60
60
  `--published` makes the hook run via the package runner (`bunx`/`npx`), so it works on
61
61
  any machine — nothing to keep in your repo but the one settings entry.
62
62
 
63
- ### Option B — from a local clone (for hacking on it)
64
-
65
- ```bash
66
- git clone https://github.com/pavlo-petrychenko/claude-plan-review
67
- cd claude-plan-review
68
- bun install # or: npm install
69
- bun src/cli.js init /path/to/your/project # …or…
70
- node src/cli.js init /path/to/your/project # writes an absolute-path hook command
71
- ```
72
63
 
73
64
  ### All projects at once (global)
74
65
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plan-review",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Review, comment on, and approve/reject Claude Code plans in a local GitHub-style web UI — with persistent comments, version history, and diffs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -73,11 +73,13 @@ export function api(method, path, body) {
73
73
  }
74
74
  }
75
75
  const errText = `${r.stderr}${r.stdout}`.trim();
76
- // gh surfaces a "(HTTP 404)"/"(HTTP 403)" + scope hint when the token can't touch gists
77
- const needsGistScope =
78
- /gist/i.test(errText) && /(scope|HTTP 40[34]|Not Found|requires)/i.test(errText);
76
+ const httpStatus = Number(errText.match(/HTTP (\d{3})/)?.[1]) || null;
77
+ // A genuine missing-scope error names the scope explicitly ("requires the
78
+ // 'gist' scope"). A bare 404 just means the gist is gone — don't conflate them.
79
+ const needsGistScope = /gist/i.test(errText) && /scope|requires/i.test(errText);
79
80
  return {
80
81
  ok: false,
82
+ httpStatus,
81
83
  error: errText || (r.spawnError ? String(r.spawnError.message || r.spawnError) : "gh failed"),
82
84
  needsGistScope,
83
85
  };
@@ -47,12 +47,15 @@ export function available() {
47
47
  }
48
48
 
49
49
  function ghError(res) {
50
+ // Only blame the scope when the token actually lacks it — otherwise surface
51
+ // gh's real error so a deleted gist / network failure isn't misdiagnosed.
52
+ const missingScope = res.needsGistScope && !gh.status().scopes.includes("gist");
50
53
  const e = new Error(
51
- res.needsGistScope
52
- ? `GitHub rejected the gist write — your gh token likely lacks the 'gist' scope. Run: ${REFRESH_CMD}`
54
+ missingScope
55
+ ? `GitHub rejected the gist write — your gh token lacks the 'gist' scope. Run: ${REFRESH_CMD}`
53
56
  : res.error || "gh request failed",
54
57
  );
55
- e.fixCommand = res.needsGistScope ? REFRESH_CMD : null;
58
+ e.fixCommand = missingScope ? REFRESH_CMD : null;
56
59
  return e;
57
60
  }
58
61
 
@@ -87,6 +90,10 @@ export function update({ id: gistId, markdown, description, filename, oldFilenam
87
90
  description: description ?? "",
88
91
  files: { [fileKey]: filePatch },
89
92
  });
93
+ // The bound gist was deleted on GitHub — recreate one and re-bind instead of failing.
94
+ if (!res.ok && res.httpStatus === 404) {
95
+ return create({ markdown, description, filename: newName });
96
+ }
90
97
  if (!res.ok) throw ghError(res);
91
98
  return {
92
99
  id: res.data.id,
package/src/cli.js CHANGED
@@ -35,12 +35,13 @@ function chooseRuntime(args) {
35
35
 
36
36
  function hookCommand(args) {
37
37
  const runtime = chooseRuntime(args);
38
- // global install → use the bare `claude-plan-review` command (resolved via PATH at hook time).
39
- // We intentionally do NOT hardcode an absolute path: version managers like fnm/nvm expose
40
- // bins via per-shell paths that don't persist across sessions.
38
+ // global install → always route through the package runner (npx/bunx), never the bare
39
+ // `claude-plan-review` command. Version managers like fnm/nvm expose global bins only via a
40
+ // per-shell PATH set up by interactive shell init. `canRun` succeeds here (init runs in an
41
+ // interactive shell) but Claude Code fires hooks in a NON-interactive shell where that PATH
42
+ // is absent, so the bare command resolves at init time yet fails — silently — at hook time.
43
+ // `npx`/`bunx` are inherited reliably from Claude Code's launching env, so they work either way.
41
44
  if (args.includes("--global")) {
42
- if (canRun("claude-plan-review")) return "claude-plan-review hook";
43
- // not on PATH → fall back to the package runner
44
45
  return runtime === "node" ? "npx claude-plan-review hook" : "bunx claude-plan-review hook";
45
46
  }
46
47
  if (args.includes("--published") || args.includes("--bunx")) {