@hardlydifficult/github
v1.0.12
Published
Typed GitHub API client wrapping Octokit with a chainable API.
Readme
@hardlydifficult/github
Typed GitHub API client wrapping Octokit with a chainable API.
Install
npm install @hardlydifficult/githubUsage
import { GitHubClient } from "@hardlydifficult/github";
// Create client — token defaults to GH_PAT env var
const github = await GitHubClient.create();
const github = await GitHubClient.create("ghp_...");
// Repo-level
const repo = github.repo("owner", "repo");
const prs = await repo.getOpenPRs();
const repoInfo = await repo.get();
// PR-level (chainable from repo)
const pr = repo.pr(42);
const data = await pr.get();
const diff = await pr.getDiff();
const files = await pr.getFiles();
const commits = await pr.getCommits();
const reviews = await pr.getReviews();
const comments = await pr.getComments();
const checkRuns = await pr.getCheckRuns();
const timeline = await pr.getTimeline(); // merged comments + reviews + commits
await pr.postComment("LGTM!");
await pr.merge("feat: my feature (#42)");
// Owner-level
const repos = await github.getOwnerRepos("owner");
// User-level (uses auto-resolved username)
const contributed = await github.getContributedRepos(30);
const myPRs = await github.getMyOpenPRs();Watching for PR activity
Poll repos and your open PRs for real-time updates — no webhooks required.
const watcher = github.watch({
repos: ["owner/repo1", "owner/repo2"],
myPRs: true,
intervalMs: 30_000, // default
});
watcher.onNewPR((event) => {
console.log(`New PR: ${event.pr.title} in ${event.repo.owner}/${event.repo.name}`);
});
watcher.onComment((event) => {
console.log(`${event.comment.user.login} commented on #${event.pr.number}`);
});
watcher.onReview((event) => {
console.log(`${event.review.user.login} ${event.review.state} #${event.pr.number}`);
});
watcher.onCheckRun((event) => {
console.log(`${event.checkRun.name}: ${event.checkRun.status} (${event.checkRun.conclusion})`);
});
watcher.onPRUpdated((event) => {
if (event.changes.draft) {
console.log(`PR #${event.pr.number} ${event.changes.draft.to ? "converted to draft" : "marked ready"}`);
}
});
watcher.onMerged((event) => {
console.log(`PR #${event.pr.number} was merged`);
});
watcher.onClosed((event) => {
console.log(`PR #${event.pr.number} was closed`);
});
watcher.onPollComplete((event) => {
console.log(`Poll complete — tracking ${event.prs.length} PRs`);
});
watcher.onError((error) => {
console.error("Watcher error:", error);
});
await watcher.start(); // initial poll + begins interval
watcher.stop(); // stop pollingThe first poll fires onNewPR for all existing open PRs (discovery). Subsequent polls fire granular events for new comments, reviews, check run changes, metadata updates (draft, labels, mergeable state), and state transitions.
API
GitHubClient
| Method | Description |
|--------|-------------|
| static create(token?) | Create client (token defaults to GH_PAT env var) |
| repo(owner, name) | Get a RepoClient scoped to owner/repo |
| watch(options) | Create a PRWatcher for polling PR activity |
| getOwnerRepos(owner) | List repos for a user or org |
| getContributedRepos(days) | Find repos the user contributed to recently |
| getMyOpenPRs() | Find open PRs by the authenticated user |
RepoClient
| Method | Description |
|--------|-------------|
| pr(number) | Get a PRClient scoped to a pull request |
| getOpenPRs() | List open pull requests |
| get() | Get repository info |
PRClient
| Method | Description |
|--------|-------------|
| get() | Get pull request details |
| getDiff() | Get PR diff as string |
| getFiles() | List files changed in the PR |
| getCommits() | List commits in the PR |
| getReviews() | List reviews on the PR |
| getComments() | List comments on the PR |
| getTimeline() | Merged timeline of comments, reviews, and commits (sorted) |
| getCheckRuns() | List check runs (auto-resolves head SHA) |
| postComment(body) | Post a comment on the PR |
| merge(title) | Squash-merge the PR |
PRWatcher
Created via github.watch(options). All on* methods return an unsubscribe function.
| Method | Description |
|--------|-------------|
| onNewPR(callback) | New PR appeared in a watched repo or user's PRs |
| onComment(callback) | New comment posted on a tracked PR |
| onReview(callback) | New review submitted on a tracked PR |
| onCheckRun(callback) | Check run created or status changed on a tracked PR |
| onPRUpdated(callback) | PR metadata changed (draft, labels, mergeable state) |
| onMerged(callback) | Tracked PR was merged |
| onClosed(callback) | Tracked PR was closed without merge |
| onPollComplete(callback) | Poll cycle finished — receives snapshot of all tracked PRs |
| onError(callback) | Polling or callback error |
| start() | Begin polling (initial poll + interval) |
| stop() | Stop polling |
| getWatchedPRs() | Returns current snapshot of all tracked PRs |
| addRepo(repo) | Start watching a new repo ("owner/repo" format) |
| removeRepo(repo) | Stop watching a repo |
WatchOptions
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| repos | string[] | [] | Repos to watch ("owner/repo" format) |
| myPRs | boolean | false | Also watch all open PRs by the authenticated user |
| intervalMs | number | 30000 | Polling interval in milliseconds |
Types
PullRequest, Repository, User, CheckRun, PullRequestReview, PullRequestComment, PullRequestFile, PullRequestCommit, Label, ContributionRepo, MergeableState, WatchOptions, PREvent, CommentEvent, ReviewEvent, CheckRunEvent, PRUpdatedEvent, PollCompleteEvent, TimelineEntry, TimelineEntryKind
Timeline Utilities
| Function | Description |
|----------|-------------|
| buildTimeline(comments, reviews, commits) | Merge PR data into a sorted TimelineEntry[] |
| formatTimeline(entries) | Render timeline as readable markdown text |
These are also available standalone (no PRClient needed) for custom pipelines.
