Skip to content

Github Notes Agent

Working through the GitHub Issues / PR interface and updating Obsidian and mdbook notes.


An end-to-end GitHub bot design GitHub Apps Docs.

When someone types @the-bot in an issue or PR, the bot summarizes the discussion with Claude Code, writes or updates an mdBook chapter, opens a PR, posts a preview link to rendered docs, and shows a nice diff. When someone types @merge, the bot merges to main and CI/CD updates the live book. Claude Code Actions docs Pulls API actions/deploy-pages.

  1. @the-bot mention: issue_comment triggers a workflow. The job aggregates the thread text, calls Claude Code to propose or update a chapter, then commits to a new branch and opens a PR. Actions events Claude Code Actions docs Pulls API
  2. Preview: on that PR, a preview site is deployed and the URL is posted via comment and Deployment Status. Deploy PR Preview Deployment Status
  3. @merge mention: a second issue_comment workflow validates permissions, then merges the PR and triggers the production deploy. Actions events Pulls API
.github/workflows/bot.yml
name: mdBook bot
on:
issue_comment:
types: [created]
permissions:
contents: write
pull-requests: write
deployments: write
pages: write
id-token: write
jobs:
handle-comment:
if: contains(github.event.comment.body, '@the-bot')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 1) Gather context for Claude Code
- name: Build prompt from thread
id: prompt
run: |
jq -r '"Issue: " + .issue.title + "\n\nComments:\n" + ([.comments[]?.body] | join("\n\n"))' \
<(gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments) \
> prompt.txt
env:
GH_TOKEN: ${{ github.token }}
# 2) Ask Claude Code to draft/update a chapter under src/
- name: Claude Code - propose mdBook page
uses: anthropics/claude-code-action@v1
with:
provider: anthropic
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
command: |
You are updating an mdBook. From prompt.txt, propose a new or updated chapter under src/.
Use concise headings, keep frontmatter minimal, and add "\n\n<!-- generated by the-bot -->" at the end.
context_path: .
extra_files: prompt.txt
# 3) Commit changes and open a PR
- name: Create PR
uses: peter-evans/create-pull-request@v6
with:
commit-message: "docs(mdbook): update from #${{ github.event.issue.number }}"
branch: bot/mdbook-${{ github.event.issue.number }}
title: "mdBook: update from #${{ github.event.issue.number }}"
body: "Automated doc update generated from the discussion."
labels: "docs,bot"
# 4) Comment back with PR link
- name: Comment with PR link
if: steps.create_pr.outputs.pull-request-number != ''
run: gh issue comment ${{ github.event.issue.number }} \
--body "Opened PR #${{ steps.create_pr.outputs.pull-request-number }} for review."
env:
GH_TOKEN: ${{ github.token }}

This listens for issue_comment, invokes Claude Code, and opens a PR with the proposed src/*.md. Replace the prompt to match your house style. Actions events Claude Code Actions docs Create Pull Request action

Choose one of the preview strategies. Here is a GitHub Pages approach that also uploads an HTML diff page.

.github/workflows/preview.yml
name: PR preview (mdBook)
on:
pull_request:
types: [opened, reopened, synchronize]
permissions:
contents: read
pages: write
id-token: write
deployments: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: peaceiris/actions-mdbook@v2
- name: Build mdBook
run: mdbook build
- name: Generate pretty diff
run: |
npm i -g diff2html-cli
git fetch origin ${{ github.base_ref }} --depth=1
git diff origin/${{ github.base_ref }}... -- src | diff2html -s side -F book/book-diff.html
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: book
deploy:
needs: build
environment:
name: pr-${{ github.event.number }}
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- id: deployment
uses: actions/deploy-pages@v4
- name: Create deployment status with preview URL
run: |
gh api repos/${{ github.repository }}/deployments \
-f ref=${{ github.head_ref }} -f environment=pr-${{ github.event.number }} \
-f required_contexts[]=
gh api repos/${{ github.repository }}/deployments/$DEP_ID/statuses \
-f state=success -f environment_url="${{ steps.deployment.outputs.page_url }}"
env:
GH_TOKEN: ${{ github.token }}

This builds the book, adds a book-diff.html generated by diff2html, deploys to Pages as a PR preview, and publishes the preview URL in the PR’s deployments UI. Swap to Cloudflare Pages or Netlify if you prefer their automatic per-PR URLs. mdBook Actions diff2html-cli actions/deploy-pages Cloudflare Pages previews Netlify Deploy Previews

.github/workflows/merge.yml
name: Merge by comment
on:
issue_comment:
types: [created]
permissions:
pull-requests: write
contents: write
jobs:
merge:
if: contains(github.event.comment.body, '@merge') && github.event.issue.pull_request
runs-on: ubuntu-latest
steps:
- name: Merge the PR
run: |
PR=${{ github.event.issue.number }}
gh pr merge $PR --squash --auto --delete-branch
env:
GH_TOKEN: ${{ github.token }}

The guard ensures the comment is on a PR. You can add extra checks for commenter role or labels. Actions events Pulls API

If you prefer a server instead of pure Actions, scaffold a Probot app to parse commands and orchestrate Actions or the Anthropic API directly. Probot Docs GitHub Apps Docs

// src/app.ts - Probot sketch for @the-bot and @merge
import { Probot } from "probot";
import { Octokit } from "@octokit/rest";
export = (app: Probot) => {
app.on("issue_comment.created", async (ctx) => {
const body = ctx.payload.comment.body || "";
const isPR = !!ctx.payload.issue.pull_request;
if (/@the-bot\b/i.test(body)) {
// 1) Aggregate thread text
const { data: comments } = await ctx.octokit.issues.listComments(ctx.issue());
const prompt = `Issue: ${ctx.payload.issue.title}\n\nComments:\n${comments.map(c => c.body).join("\n\n")}`;
// 2) Call Claude (server-to-server) to generate markdown content here...
// 3) Commit file and open PR
await ctx.octokit.repos.createOrUpdateFileContents({
...ctx.repo(),
path: `src/ai/${ctx.payload.issue.number}.md`,
message: `docs(mdbook): AI draft for #${ctx.payload.issue.number}`,
content: Buffer.from(`# ${ctx.payload.issue.title}\n\n${prompt}\n\n<!-- generated by the-bot -->`).toString("base64"),
branch: `bot/mdbook-${ctx.payload.issue.number}`,
});
const pr = await ctx.octokit.pulls.create({ ...ctx.repo(), head: `bot/mdbook-${ctx.payload.issue.number}`, base: "main", title: `mdBook: update from #${ctx.payload.issue.number}` });
await ctx.octokit.issues.createComment({ ...ctx.issue(), body: `Opened PR #${pr.data.number}` });
}
if (/@merge\b/i.test(body) && isPR) {
await ctx.octokit.pulls.merge({ ...ctx.repo(), pull_number: ctx.payload.issue.number, merge_method: "squash" });
}
});
};

Probot handles webhook validation and App auth, while Octokit writes files and merges PRs. Probot Docs Contents API Pulls API

Once merged to main, run a Pages or Cloudflare deploy job that builds mdBook and publishes. actions/deploy-pages Cloudflare Pages previews

  • Turn on Read and write workflow permissions and explicitly grant job-level scopes: contents: write, pull-requests: write, deployments: write. Also enable “Allow GitHub Actions to create and approve pull requests” where needed. Actions workflow permissions
  • Restrict @merge to trusted roles or require a label like approved-by-human before merging. Pulls API
  • For previews on external hosts, post the preview URL back to the PR and optionally create a Deployment entry for discoverability. Deployments Deployment Status
  • Fast to ship: the Actions-only path uses an off-the-shelf Claude Code Action and popular marketplace actions.
  • Portable: switching preview provider is one variable in the workflow.
  • Traceable: PRs carry the bot’s draft, a rendered preview, and a readable HTML diff. Claude Code Actions docs Deploy PR Preview diff2html