Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions DESIGN_BRIEF.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@ interrupt an engineer to translate for you.

## How it actually works (for an accurate "how it works" section — don't invent steps)

1. The user writes a short `learning-profile.md` once — their role,
and what they're currently trying to understand better. Not a
1. The user runs `pr-explainer init` once — it creates
`~/.pr-explainer/learning-profile.md`. They edit it with their
role and what they're currently trying to understand better. Not a
skills checklist, just a couple of sentences. (e.g. "Product
analyst, don't read code, need to explain shipped changes to
stakeholders" or "Backend engineer, 2 years, new to frontend.")
That one file applies everywhere they run the CLI.
2. They run one command against any merged PR — a bare number, a full
GitHub URL, or `owner/repo#123`:
```
Expand All @@ -69,12 +71,10 @@ interrupt an engineer to translate for you.

## The key differentiator (must appear prominently — this is the actual hook)

**No API key, no signup, no billing.** Most AI PR-summarizer tools
require you to bring your own API key and pay per call. pr-explainer
instead shells out to the Claude Code CLI already on the user's
machine — so if they already use Claude Code (subscription or key,
either works), this is free to try immediately. Nothing new to sign up
for.
**No API key, no signup, no billing.** pr-explainer shells out to the
Claude Code CLI already on the user's machine — so if they already use
Claude Code (subscription or key, either works), this is free to try
immediately. Nothing new to sign up for, nothing to pay per call.

Secondary differentiators:
- Works on **any repo**, not just ones you own — point it at a URL
Expand Down Expand Up @@ -116,7 +116,7 @@ Avoid generic robot/AI-brain iconography — this product is about
## Landing page — sections to include

1. **Hero:** tagline + one-line explanation + the install command
(`npm install -g pr-explainer`) + primary CTA linking to GitHub repo.
(`npm install -g @shilpi1958/pr-explainer`) + primary CTA linking to GitHub repo.
2. **The thesis:** short version of "code is the source of truth, this
removes the translation barrier" — 2-3 sentences, not a manifesto.
3. **How it works:** the 4 steps above, ideally with a real example
Expand All @@ -125,7 +125,7 @@ Avoid generic robot/AI-brain iconography — this product is about
4. **Who it's for:** the three scenarios above, as short cards/sections.
5. **Why no API key:** the differentiator, explained briefly.
6. **Install / quickstart:** copy-pasteable commands, matching the
README (`npm install -g pr-explainer`, copy the profile template,
README (`npm install -g @shilpi1958/pr-explainer`, `pr-explainer init`,
run one command).
7. **Footer:** link to GitHub repo, license (MIT), maybe a link to
Product Hunt listing once live.
Expand Down
28 changes: 23 additions & 5 deletions LAUNCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Code is the source of truth. Now anyone can read it.
> and logged in (subscription or key), pr-explainer rides on that —
> nothing new to sign up for, no separate billing.
>
> Free, open source, self-hosted. `npm install -g pr-explainer`.
> Free, open source, self-hosted. `npm install -g @shilpi1958/pr-explainer`.

**First maker comment (post immediately after launch):**

Expand Down Expand Up @@ -139,11 +139,12 @@ Code is the source of truth. Now anyone can read it.

**Thread (reply 4 — the differentiator):**

> Most PR-summarizer tools need you to bring an API key and pay
> per-call. This one shells out to your local `claude` CLI — so if
> you're already a Claude Code user, it's free to try, right now.
> No API key, no per-call billing. This shells out to your local
> `claude` CLI — so if you're already a Claude Code user, it's free
> to try, right now.
>
> `npm install -g pr-explainer`
> `npm install -g @shilpi1958/pr-explainer`
> `pr-explainer init` # then edit ~/.pr-explainer/learning-profile.md

**Thread (reply 5 — CTA + link):**

Expand All @@ -165,3 +166,20 @@ and early Twitter traffic hitting the PH page while it's fresh helps that.
- **Hacker News (Show HN):** title format `Show HN: pr-explainer – explain any GitHub PR in plain English, pitched to you`. HN audience is technical-skeptical — lead with the mechanism (shells out to `claude -p`, no separate API key) rather than the "code is source of truth" framing, which can read as overwrought to that crowd. Expect blunt feedback on the quiz feature's usefulness.
- **r/programming or r/ExperiencedDevs:** only if genuinely participating, not just dropping a link — these subreddits penalize drive-by self-promotion.
- **dev.to / Hashnode post:** "Code is the only source of truth that doesn't go stale — here's a tool that makes it readable by anyone" — good place for the longer narrative version of the maker comment, links back to PH/GitHub.

---

## Pre-flight checklist (before PH goes live)

Install note: the npm package is `@shilpi1958/pr-explainer` (the bare
name `pr-explainer` on npm is a different project). CLI command stays
`pr-explainer`. Profile lives at `~/.pr-explainer/learning-profile.md`
after `pr-explainer init` — one file, every directory.

- [ ] `@shilpi1958/pr-explainer` is live on npm; `npm install -g` from a clean machine works
- [ ] `pr-explainer init` creates `~/.pr-explainer/learning-profile.md`; explaining a PR from `$HOME` works with no local profile
- [ ] GitHub Pages is serving `docs/index.html` with the scoped install + `init` commands
- [ ] PH gallery ready: install + init, a real generated explainer, quiz section visible
- [ ] PH + Twitter go live together (12:01am PT) — not Twitter-first
- [ ] Maker comment + Show HN title ready to paste
- [ ] Tag / release `v0.2.0` on GitHub so Action consumers have a clear version
60 changes: 47 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,34 @@ input that changes is a one-time profile describing who's reading.

## How it works

1. You write a `learning-profile.md` once — your role and what you're
currently trying to understand better. Not a skills checklist,
just a couple of sentences.
1. Run `pr-explainer init` once — it creates
`~/.pr-explainer/learning-profile.md`. Edit it with your role and
what you're currently trying to understand better. Not a skills
checklist, just a couple of sentences. That one file applies
everywhere you run the CLI.
2. Point the CLI at any merged PR — a number, a URL, whatever `gh pr
view` accepts. It reads the PR's diff and description, combines it
with your profile, and asks Claude to write one plain-language
explainer: what changed, why it was done this way, why it matters
to you — followed by a couple of recall questions with answers.
3. The explainer lands in `docs/explainers/`, and gets added to a
running `index.md` of every PR you've explained so far.
3. The explainer lands in `~/.pr-explainer/explainers/`, and gets added
to a running `index.md` of every PR you've explained so far.

## Quick start (CLI)

```bash
npm install -g pr-explainer
cp node_modules/pr-explainer/templates/learning-profile.example.md ./learning-profile.md
# edit learning-profile.md to describe yourself
npm install -g @shilpi1958/pr-explainer
pr-explainer init
# edit ~/.pr-explainer/learning-profile.md to describe yourself

pr-explainer 42
pr-explainer https://github.com/some-org/some-repo/pull/42 # works on any repo
pr-explainer 42 # current repo only
pr-explainer https://github.com/some-org/some-repo/pull/42 # any repo
pr-explainer some-org/some-repo#42
```

> **Note:** a bare number resolves against the GitHub repo of your current
> directory. To explain a PR elsewhere, pass the full URL or `owner/repo#N`.
> Only **merged** PRs are supported.
No API key needed if you already have [Claude Code](https://claude.com/claude-code)
installed and logged in — `pr-explainer` calls the local `claude` CLI, so it
rides on whatever auth you already use there (subscription or key). Run `claude`
Expand Down Expand Up @@ -82,14 +88,22 @@ jobs:
No subscription? Pass `anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}`
instead (Console API key, billed separately).

Every merge gets you a commit with a fresh explainer.
Every merge gets you a commit with a fresh explainer. Put a
`learning-profile.md` in the repo root for the Action (or pass
`profile-path`); explainers are written to `docs/explainers/` by default.

## Configuration

Profile lookup (first hit wins):

1. `LEARNING_PROFILE` env
2. `./learning-profile.md` (repo override — useful for the Action)
3. `~/.pr-explainer/learning-profile.md` (default for local CLI)

| Env var | Default | Purpose |
|---|---|---|
| `LEARNING_PROFILE` | `./learning-profile.md` | path to your profile |
| `EXPLAINER_DIR` | `./docs/explainers` | output directory |
| `LEARNING_PROFILE` | `~/.pr-explainer/learning-profile.md` | path to your profile |
| `EXPLAINER_DIR` | `~/.pr-explainer/explainers` | output directory |

See [`templates/learning-profile.example.md`](templates/learning-profile.example.md)
for the profile format.
Expand All @@ -101,6 +115,26 @@ Anthropic API directly. If you already use Claude Code, you already have
auth configured — nothing new to sign up for, no data leaving your
machine except what `claude` itself sends.

## Troubleshooting

| Symptom | Fix |
|---|---|
| `No profile found` | Run `pr-explainer init`, then edit `~/.pr-explainer/learning-profile.md` |
| `Could not resolve to a PullRequest` / PR not found | A bare number only looks in the **current** repo. Pass a URL or `owner/repo#42` |
| `PR … is not merged yet` | Only merged PRs are supported — pick one that already shipped |
| `claude -p failed` / spending cap / usage limit | Wait for the Claude Code reset, raise your cap, or retry later. Confirm `claude` works on its own |
| Claude Code CLI not found | Install from https://claude.com/claude-code and run `claude` once to log in |
| `gh` auth / forbidden errors | Run `gh auth login` |
| GitHub CLI not found | Install from https://cli.github.com/ |

Quick sanity checks:

```bash
gh auth status
claude -p --output-format text <<< "Say hi in one word"
pr-explainer --help
```

## License

MIT
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ runs:
- name: Install pr-explainer and Claude Code CLI
shell: bash
run: |
npm install --no-save pr-explainer @anthropic-ai/claude-code
npm install --no-save @shilpi1958/pr-explainer @anthropic-ai/claude-code

- name: Generate explainer
id: generate
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 24 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "pr-explainer",
"version": "0.2.0",
"name": "@shilpi1958/pr-explainer",
"version": "0.2.1",
"description": "Point it at any merged PR and get a plain explainer of what it did and why, pitched at you.",
"type": "module",
"bin": {
"pr-explainer": "./src/cli.js"
"pr-explainer": "src/cli.js"
},
"files": [
"src",
Expand All @@ -13,7 +13,28 @@
"scripts": {
"start": "node src/cli.js"
},
"keywords": [
"github",
"pull-request",
"pr",
"explainer",
"claude",
"cli",
"documentation"
],
"author": "shilpi1958",
"repository": {
"type": "git",
"url": "git+https://github.com/shilpi1958/pr-explainer.git"
},
"homepage": "https://github.com/shilpi1958/pr-explainer#readme",
"bugs": {
"url": "https://github.com/shilpi1958/pr-explainer/issues"
},
"license": "MIT",
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=18"
}
Expand Down
35 changes: 33 additions & 2 deletions src/claude.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { execFile } from "node:child_process";

function friendlyClaudeError(detail) {
const text = detail || "";
const lower = text.toLowerCase();

if (/spending cap|usage limit|rate limit|quota/i.test(text)) {
return (
`${text.trim()}\n\n` +
`Your Claude Code plan has hit its usage limit. Wait for the reset, ` +
`raise the cap in your Claude account, or try again later.`
);
}
if (/not logged in|unauthorized|authentication|please run.*login|login required/i.test(lower)) {
return (
`${text.trim()}\n\n` +
`Run \`claude\` once in your terminal to log in, then retry.`
);
}
return text.trim() || "unknown error from claude CLI";
}

export function runClaude(prompt, { model } = {}) {
const args = ["-p", "--output-format", "text"];
if (model) args.push("--model", model);
Expand All @@ -20,10 +40,21 @@ export function runClaude(prompt, { model } = {}) {
);
return;
}
reject(new Error(`claude -p failed: ${stderr || err.message}`));
const detail = [stderr, stdout]
.map((s) => s?.trim())
.filter(Boolean)
.join("\n");
reject(new Error(`claude -p failed: ${friendlyClaudeError(detail || err.message)}`));
return;
}

const out = (stdout || "").trim();
// Some limit/auth failures exit 0 but only print a short status line.
if (!out || (/spending cap|usage limit|rate limit|quota|not logged in/i.test(out) && !/^#\s/m.test(out))) {
reject(new Error(`claude -p failed: ${friendlyClaudeError(out || "empty response")}`));
return;
}
resolve(stdout.trim());
resolve(out);
}
);
child.stdin.write(prompt);
Expand Down
Loading
Loading