Turns #123 in Claude's replies into a clickable link to that PR on GitHub.

A Claude Code plugin marketplace with my mods.
claude plugin marketplace add AydinHassan/claude-mods
claude plugin install pr-links@aydin-mods
Turns #123 in Claude's replies into a clickable link to that pull request on GitHub.
The repository is read from git remote get-url origin in the session's working directory. Links point at /pull/123, which GitHub redirects to the issue when the number belongs to an issue.
Limits:
gh pr list result, is left as it is.#333 outside code is linked like a PR number.hooks/register.ts 28 lines1import type { Register } from 'claude-code'
2import { linkify, repoUrlFromRemote } from './linkify'
3
4export const register: Register = on => {
5 const repoUrls = new Map<string, Promise<string | undefined>>()
6
7 on('ui.render', { component: 'AssistantMessage' }, async ($, e, next) => {
8 if (!/#\d/.test(e.props.text)) {
9 return next(e)
10 }
11
12 const cwd = await $.session.cwd()
13 let repoUrl = repoUrls.get(cwd)
14
15 if (repoUrl === undefined) {
16 repoUrl = $.process
17 .run(['git', 'remote', 'get-url', 'origin'], { cwd })
18 .then(ran => (ran.exitCode === 0 ? repoUrlFromRemote(ran.stdout) : undefined))
19 .catch(() => undefined)
20 repoUrls.set(cwd, repoUrl)
21 }
22
23 const url = await repoUrl
24
25 return url === undefined ? next(e) : next({ ...e, props: { ...e.props, text: linkify(e.props.text, url) } })
26 })
27}
28hooks/linkify.ts 17 lines1// Code fences, inline code, existing markdown links and bare URLs are matched
2// first so a number inside them is left alone.
3const PATTERN =
4 /(```[\s\S]*?(?:```|$)|~~~[\s\S]*?(?:~~~|$)|`[^`\n]*`|\[[^\]\n]*\]\([^)\n]*\)|https?:\/\/\S+)|(?<![\w&#/])#(\d{1,6})\b/g
5
6export function linkify(text: string, repoUrl: string): string {
7 return text.replace(PATTERN, (match, skipped: string | undefined, number: string) =>
8 skipped === undefined ? `[#${number}](${repoUrl}/pull/${number})` : match,
9 )
10}
11
12export function repoUrlFromRemote(remote: string): string | undefined {
13 const found = /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?\s*$/.exec(remote)
14
15 return found === null ? undefined : `https://github.com/${found[1]}/${found[2]}`
16}
17