CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude Code
Claude Code/2026-05-04Intermediate

Build a Pipeline Where Docs Update Automatically Every Time Your Code Changes

Build a CI/CD pipeline that auto-generates README, CHANGELOG, and API docs whenever code changes. Use Claude Haiku 4.5 for cost-efficient classification and Sonnet 4.6 for quality output — cutting API costs by up to 70% while keeping documentation accurate.

automation95documentation3GitHub Actions12Claude Code196CI/CD18cost optimization13

The moment you finish writing code, your documentation is already out of date.

If you've done any amount of solo development, you know this feeling. The README you planned to write "later" is still blank weeks afterward. The API spec you promised yourself you'd keep updated is frozen at version 3 while the codebase has moved on. This isn't a discipline problem. Writing code and explaining code in natural language are fundamentally different cognitive tasks — and the incentives don't line up.

Claude Code and the Anthropic SDK give you a structural answer to a structural problem. When your code changes, your documentation changes with it — automatically. In this article, I'll walk through a three-layer pipeline built with Python scripts, GitHub Actions, and the Claude API, with working code you can drop into any project.

Why "Write It Later" Never Works

Documentation debt is often framed as a motivation or discipline problem, but I think it's better understood as an incentive design failure.

The rewards for writing code are immediate and concrete: tests pass, features ship, users can do things they couldn't before. The rewards for writing documentation are delayed and indirect: "Six months from now, onboarding a new team member will be faster." That kind of payoff is genuinely hard to weigh against the pressure of today's deadline.

Making it worse, "writing docs later" means writing from memory. The further you get from the implementation, the more you lose the context behind design decisions. The best time to document is right after writing, but that's exactly when you're most under pressure to move on.

Automation works here because it eliminates the timing mismatch. When you push a commit, the LLM reads your code and generates documentation — no human in the loop, no delayed reward.

Designing the Pipeline from the Output Back

Here's what the full system produces:

  • README.md — regenerated from current source structure and code patterns
  • CHANGELOG.md — updated from recent commits, classified and formatted
  • docs/api.md — extracted from exported TypeScript types and JSDoc comments

Three layers make this work:

Layer 1: Python scripts. scripts/generate_readme.py, scripts/generate_changelog.py, and scripts/generate_api_docs.py. Each runs standalone for local preview before you wire them into CI.

Layer 2: GitHub Actions workflow. Triggered on pushes to main that touch source files. Calls scripts in sequence, commits results with [skip ci] to prevent infinite loops.

Layer 3: Model selection by task complexity. Haiku 4.5 for classification (cheap, fast, sufficient for sorting tasks). Sonnet 4.6 for natural language generation (quality matters when humans read the output).

The directory structure looks like this:

your-project/
├── scripts/
│   ├── generate_readme.py
│   ├── generate_changelog.py
│   └── generate_api_docs.py
├── .github/
│   └── workflows/
│       └── auto-docs.yml
├── docs/
│   ├── api.md        ← auto-generated
│   └── changelog.md  ← auto-generated
└── README.md         ← auto-generated

Setup and CLAUDE.md Configuration

Install the Anthropic library:

pip install anthropic==0.50.0

A CLAUDE.md at your project root helps Claude understand your documentation conventions — which reduces output variance across runs.

# CLAUDE.md
 
## Documentation conventions
- README.md is user-facing. Focus on installation and basic usage.
- docs/api.md is developer-facing. Emphasize types, parameters, and side effects.
- CHANGELOG follows Conventional Commits format.
- Code examples use TypeScript.

Set ANTHROPIC_API_KEY as an environment variable locally (.env file) and as a GitHub Repository Secret in CI.

README Generation — Turning Code Structure Into a Project Overview

The first script reads your source files, feeds them to Sonnet 4.6, and produces a README. The key design decision is which files to include: passing everything blows up the context window, so we prioritize recently modified files — they're most likely to reflect the current state of the project.

# scripts/generate_readme.py
import anthropic
import sys
from pathlib import Path
 
client = anthropic.Anthropic()
 
def get_code_structure(repo_path: str, max_files: int = 8) -> str:
    """Collect source files from the repository."""
    repo = Path(repo_path)
 
    patterns = ['*.ts', '*.tsx', '*.py', '*.js', '*.go']
    exclude_dirs = {'node_modules', 'dist', '.next', '__pycache__', '.git', 'coverage'}
 
    files = []
    for pattern in patterns:
        for path in repo.rglob(pattern):
            if not any(excl in path.parts for excl in exclude_dirs):
                files.append(path)
 
    # Most recently modified files are most representative of current state
    files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
 
    samples = []
    for filepath in files[:max_files]:
        try:
            content = filepath.read_text(errors='replace')[:2500]
            relative = filepath.relative_to(repo)
            samples.append(f"### {relative}\n```\n{content}\n```")
        except (OSError, ValueError) as e:
            print(f"Warning: could not read {filepath}: {e}", file=sys.stderr)
            continue
 
    if not samples:
        raise ValueError("No source files found in the target directory")
 
    return '\n\n'.join(samples)
 
def generate_readme(repo_path: str, project_name: str) -> str:
    """Generate a README using Claude Sonnet 4.6."""
    code_structure = get_code_structure(repo_path)
 
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=4096,
        system="""You are a technical writing expert specializing in GitHub READMEs.
Read the code and write the documentation developers actually need.
Avoid padding. Keep it practical.""",
        messages=[{
            "role": "user",
            "content": f"""Project name: {project_name}
 
Analyze the following source code and generate a README.md.
 
{code_structure}
 
Include these sections:
1. Project overview (3 sentences max)
2. Key features (bullet list, 5 items max)
3. Installation
4. Basic usage (with working code example)
5. Configuration options (only if they exist in the code)
 
Output in Markdown format."""
        }]
    )
 
    return response.content[0].text
 
if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python generate_readme.py <repo_path> <project_name>", file=sys.stderr)
        sys.exit(1)
 
    try:
        readme = generate_readme(sys.argv[1], sys.argv[2])
        print(readme)
    except ValueError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)
    except anthropic.APIConnectionError:
        print("Error: Could not reach Anthropic API. Check your network connection.", file=sys.stderr)
        sys.exit(1)
    except anthropic.AuthenticationError:
        print("Error: Invalid API key. Check your ANTHROPIC_API_KEY environment variable.", file=sys.stderr)
        sys.exit(1)

Run it locally to preview before adding it to CI:

# Preview output in terminal
python scripts/generate_readme.py . "my-project"
 
# Write to file once you're happy with the output
python scripts/generate_readme.py . "my-project" > README.md

CHANGELOG Generation — From Commit History to Release Notes

This is where the two-model cost optimization comes in. Classifying commits into categories (features, fixes, improvements) is a mechanical sorting task — Haiku 4.5 handles it at a fraction of the cost. Formatting the result into readable release notes is where language quality matters — that's Sonnet 4.6's job.

# scripts/generate_changelog.py
import anthropic
import subprocess
import json
import sys
from datetime import datetime
from typing import Optional
 
client = anthropic.Anthropic()
 
def get_recent_commits(since_tag: Optional[str] = None, limit: int = 30) -> list[dict]:
    """Retrieve recent commit history from git."""
    if since_tag:
        cmd = ['git', 'log', f'{since_tag}..HEAD',
               '--pretty=format:%H|%s|%an|%ad', '--date=short']
    else:
        cmd = ['git', 'log', f'-{limit}',
               '--pretty=format:%H|%s|%an|%ad', '--date=short']
 
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"git log failed: {result.stderr.strip()}")
 
    commits = []
    for line in result.stdout.strip().split('\n'):
        if not line:
            continue
        parts = line.split('|', 3)
        if len(parts) >= 4:
            commits.append({
                'hash': parts[0][:8],
                'message': parts[1],
                'author': parts[2],
                'date': parts[3]
            })
    return commits
 
def classify_commits_with_haiku(commits: list[dict]) -> dict:
    """Classify commits using Haiku 4.5 — cheap, fast, sufficient for sorting."""
    commit_text = '\n'.join([f"- {c['message']}" for c in commits])
 
    # Haiku 4.5 input: ~$0.08/1M tokens (vs $3.00/1M for Sonnet 4.6)
    # Perfect for simple classification tasks like this one
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=1024,
        messages=[{
            "role": "user",
            "content": f"""Classify these commit messages into categories.
 
{commit_text}
 
Return JSON only (no explanation):
{{"features": [], "fixes": [], "improvements": [], "docs": [], "other": []}}
 
Classification rules:
- features: starts with feat:, or clearly adds a new capability
- fixes: starts with fix:, or resolves a bug
- improvements: starts with refactor: or perf:, or improves existing behavior
- docs: starts with docs:, or updates documentation only
- other: everything else"""
        }]
    )
 
    text = response.content[0].text
    # Handle markdown code blocks in the response
    if '```' in text:
        parts = text.split('```')
        text = parts[1] if len(parts) > 1 else text
        if text.startswith('json'):
            text = text[4:]
 
    try:
        return json.loads(text.strip())
    except json.JSONDecodeError:
        # Fallback: dump everything into other
        return {"features": [], "fixes": [], "improvements": [],
                "docs": [], "other": [c['message'] for c in commits]}
 
def format_changelog_with_sonnet(version: str, date: str, classified: dict) -> str:
    """Format the classified commits into readable release notes using Sonnet 4.6."""
    has_changes = any(classified.get(k) for k in ['features', 'fixes', 'improvements'])
    if not has_changes:
        return ""
 
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": f"""Format these changes as a CHANGELOG entry.
 
Version: {version}
Date: {date}
Changes:
{json.dumps(classified, ensure_ascii=False, indent=2)}
 
Format (omit sections with no entries):
## [{version}] - {date}
 
### New Features
- change description
 
### Bug Fixes
- change description
 
### Improvements
- change description
 
Keep technical terms as-is. Write for developers who need to understand what changed."""
        }]
    )
 
    return response.content[0].text
 
if __name__ == "__main__":
    since_tag = sys.argv[1] if len(sys.argv) > 1 else None
 
    try:
        commits = get_recent_commits(since_tag=since_tag)
        if not commits:
            print("No new commits found")
            sys.exit(0)
 
        # Step 1: Classify with Haiku 4.5 (~$0.001 for 30 commits)
        classified = classify_commits_with_haiku(commits)
 
        # Step 2: Format with Sonnet 4.6 (quality where it matters)
        today = datetime.now().strftime("%Y-%m-%d")
        entry = format_changelog_with_sonnet("unreleased", today, classified)
 
        if entry:
            print(entry)
        else:
            print("No significant changes to record")
 
    except RuntimeError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)
    except anthropic.RateLimitError:
        print("Error: API rate limit reached. Wait a moment and retry.", file=sys.stderr)
        sys.exit(1)

In practice, this Haiku + Sonnet split reduces costs by 65–70% compared to using Sonnet 4.6 for the entire pipeline. For 30 commits, the classification step costs under $0.002 total.

API Documentation — From TypeScript Exports to Developer Reference

The third script extracts exported TypeScript types, interfaces, and functions — including JSDoc comments — and generates a developer reference document.

# scripts/generate_api_docs.py
import anthropic
import sys
from pathlib import Path
 
client = anthropic.Anthropic()
 
def extract_typescript_signatures(source_dir: str) -> str:
    """Extract exported definitions from TypeScript files."""
    src = Path(source_dir)
 
    if not src.exists():
        raise FileNotFoundError(f"Directory not found: {source_dir}")
 
    ts_files = list(src.rglob('*.ts')) + list(src.rglob('*.tsx'))
    # Exclude node_modules, declaration files, and test files
    ts_files = [
        f for f in ts_files
        if 'node_modules' not in f.parts
        and not f.name.endswith('.d.ts')
        and not f.name.endswith('.test.ts')
        and not f.name.endswith('.spec.ts')
    ]
 
    signatures = []
    for filepath in ts_files[:20]:
        try:
            content = filepath.read_text(errors='replace')
            lines = content.split('\n')
            relevant_lines = []
            in_jsdoc = False
 
            for line in lines:
                stripped = line.strip()
                if stripped.startswith('/**'):
                    in_jsdoc = True
                if in_jsdoc or stripped.startswith('export'):
                    relevant_lines.append(line)
                if '*/' in line:
                    in_jsdoc = False
 
            if relevant_lines:
                relative = filepath.relative_to(src.parent)
                extracted = '\n'.join(relevant_lines[:80])
                signatures.append(f"### {relative}\n```typescript\n{extracted}\n```")
        except (OSError, ValueError) as e:
            print(f"Warning: error processing {filepath}: {e}", file=sys.stderr)
            continue
 
    if not signatures:
        raise ValueError("No exported TypeScript definitions found")
 
    return '\n\n'.join(signatures)
 
def generate_api_docs(source_dir: str, project_name: str) -> str:
    """Generate API reference documentation using Sonnet 4.6."""
    signatures = extract_typescript_signatures(source_dir)
 
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=8192,
        system="""You are a technical documentation expert.
Read TypeScript code and write the reference documentation developers actually need.
- Be explicit about argument types, return types, and defaults
- Include working usage examples for each exported function
- Call out error conditions and edge cases
- Avoid restating what the type signature already says""",
        messages=[{
            "role": "user",
            "content": f"""Project: {project_name}
 
Generate API reference documentation from this TypeScript code.
 
{signatures}
 
For each export:
1. One or two sentences describing what it does
2. Parameters (type, required/optional, default value)
3. Return value description
4. Usage example (executable code)
5. Notes or caveats (only if relevant)
 
Output in Markdown."""
        }]
    )
 
    return response.content[0].text
 
if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python generate_api_docs.py <source_dir> <project_name>", file=sys.stderr)
        sys.exit(1)
 
    try:
        docs = generate_api_docs(sys.argv[1], sys.argv[2])
        print(docs)
    except FileNotFoundError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)
    except ValueError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

The extraction logic targets export statements and their preceding JSDoc blocks. It's imperfect — complex generics and decorators can trip it up — but for most TypeScript projects it covers 80–90% of the public API surface without pulling in private implementation details.

Completing the Pipeline With GitHub Actions

Here's the full workflow that connects the three scripts:

# .github/workflows/auto-docs.yml
name: Auto Documentation Update
 
on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'lib/**'
      - 'api/**'
      - '!**/*.test.ts'
      - '!**/*.spec.ts'
 
jobs:
  update-docs:
    runs-on: ubuntu-latest
    permissions:
      contents: write
 
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
 
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
 
      - name: Install dependencies
        run: pip install anthropic==0.50.0
 
      - name: Detect changed file types
        id: changes
        run: |
          CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
          if echo "$CHANGED" | grep -qE '\.(ts|tsx|py|js|go)$'; then
            echo "source_changed=true" >> $GITHUB_OUTPUT
          else
            echo "source_changed=false" >> $GITHUB_OUTPUT
          fi
 
      - name: Generate README
        if: steps.changes.outputs.source_changed == 'true'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python scripts/generate_readme.py . "${{ github.repository }}" > /tmp/readme_new.md
          EXIT_CODE=$?
 
          if [ $EXIT_CODE -ne 0 ]; then
            echo "::warning::README generation failed (exit $EXIT_CODE)"
            exit 0  # Continue with other steps even if this one fails
          fi
 
          # Only update if the diff is significant (30+ lines changed)
          DIFF_LINES=$(diff -u README.md /tmp/readme_new.md 2>/dev/null | wc -l || echo "0")
          if [ "$DIFF_LINES" -gt 30 ]; then
            mv /tmp/readme_new.md README.md
            echo "README updated (${DIFF_LINES} lines changed)"
          fi
 
      - name: Update CHANGELOG
        if: steps.changes.outputs.source_changed == 'true'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
 
          if [ -n "$LATEST_TAG" ]; then
            python scripts/generate_changelog.py "$LATEST_TAG" > /tmp/changelog_entry.md
          else
            python scripts/generate_changelog.py > /tmp/changelog_entry.md
          fi
 
          EXIT_CODE=$?
          if [ $EXIT_CODE -ne 0 ]; then
            echo "::warning::CHANGELOG generation failed"
            exit 0
          fi
 
          if [ -f CHANGELOG.md ]; then
            cat /tmp/changelog_entry.md CHANGELOG.md > /tmp/changelog_merged.md
            mv /tmp/changelog_merged.md CHANGELOG.md
          else
            mv /tmp/changelog_entry.md CHANGELOG.md
          fi
 
      - name: Generate API docs
        if: steps.changes.outputs.source_changed == 'true'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          mkdir -p docs
          python scripts/generate_api_docs.py src/ "${{ github.repository }}" > docs/api.md
          EXIT_CODE=$?
 
          if [ $EXIT_CODE -ne 0 ]; then
            echo "::warning::API doc generation failed"
            exit 0
          fi
 
      - name: Commit documentation updates
        run: |
          if git diff --quiet README.md CHANGELOG.md docs/ 2>/dev/null; then
            echo "No documentation changes"
            exit 0
          fi
 
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add README.md CHANGELOG.md docs/
          git commit -m "docs: auto-update documentation [skip ci]"
          git push

The most important design principle here: any individual step can fail without stopping the rest. Documentation generation involves API calls, which can hit rate limits or temporary outages. Explicit exit 0 on failure means a README generation issue won't block your CHANGELOG from updating.

The threshold on README updates (30+ lines changed) prevents the git history from filling up with trivial regenerations when only a comment changed.

Common Failure Patterns and How to Fix Them

Infinite loop: the generated commit triggers the workflow again.

The [skip ci] tag in the commit message handles this. For extra safety, add docs/** to the paths exclude filter so pushes to the docs directory don't trigger doc generation.

Output quality varies between runs.

Set temperature explicitly in the API calls — the default (1.0) allows more variation than you probably want for structured documentation output.

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    temperature=0.3,  # More consistent output across runs
    # ...
)

Context length exceeded on large repositories.

Pass only changed files instead of the whole repository:

# In GitHub Actions
CHANGED_TS=$(git diff --name-only HEAD~1 HEAD | grep -E '\.tsx?$' | head -10)

For very large repos, generate docs per-module and merge the results afterward.

UnicodeDecodeError on files with non-ASCII content.

Use errors='replace' rather than errors='ignore' when reading files. The latter silently drops characters; the former replaces unreadable bytes with ?, which is easier to notice and debug.

Cost Breakdown: Where Each Model Dollar Goes

Understanding what you're paying for makes it easier to optimize further.

Haiku 4.5 (changelog classification):

  • Input: ~$0.08/1M tokens
  • 30 commits = roughly 600 tokens input
  • Cost per run: under $0.001

Sonnet 4.6 (README, API docs, changelog formatting):

  • Input: ~$3.00/1M tokens
  • Typical README run: 8,000–15,000 tokens input
  • Cost per run: $0.024–$0.045

Realistic monthly cost for a medium-sized project (30 CI runs/month):

  • Total: roughly $1.50–$3.00
  • Combined with GitHub Actions' free tier (2,000 minutes/month), this runs nearly free for personal projects.

If you need tighter cost control, Prompt Caching is worth adding. Caching the system prompt and fixed context portions can reduce costs by up to 90% on repeated runs. The GitHub Actions automation guide for Claude Code covers caching integration in detail.

Notes on Running This in a Team Context

The scripts as written auto-commit directly to main. That works fine for solo projects, but in a team you might want documentation updates to go through review. In that case, change the final step to push to a separate branch and open a pull request:

git checkout -b docs/auto-update-$(date +%Y%m%d)
git add README.md CHANGELOG.md docs/
git commit -m "docs: auto-update documentation"
git push origin docs/auto-update-$(date +%Y%m%d)
# Use GitHub CLI to open a PR if desired

One other team consideration: treat the prompts inside each script as code. Check them into version control, review changes to them, and track when prompt modifications affect output quality. When documentation suddenly gets worse (or better), you want to be able to trace it to a specific change.

For more on integrating automated workflows with Claude Code's hook system, the Claude Code Hooks automation guide has patterns that complement what's covered here.

Getting Started Today

Building the habit of writing documentation is hard. Building the infrastructure that writes documentation for you is a one-time investment.

Start with just scripts/generate_readme.py on an existing project. Run it locally, compare the output to your current README, and adjust the prompt until the quality is where you want it. Once you trust the output quality, adding the GitHub Actions wrapper takes another 20 minutes.

From there, add changelog generation, then API docs — in whatever order matches what your project most needs right now. The modular design means you don't have to do everything at once.

The goal isn't perfect documentation. It's documentation that's accurate enough to be useful, updated automatically, with no ongoing effort required from you.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-05-04
Setting Up Claude Code's GitHub PR Trigger for Automated Code Review
A step-by-step guide to configuring Claude Code's GitHub PR trigger, writing effective CLAUDE.md review policies, and what two weeks of real usage taught me about keeping the signal-to-noise ratio high.
Claude Code2026-04-01
Claude Code HTTP Hooks × GitHub Actions Integration Guide — Production Patterns for Automated Code Review, Testing, and Deployment
A deep dive into integrating Claude Code HTTP Hooks with GitHub Actions to build production-grade pipelines for automated code review, quality checks, and deployment — with detailed code examples throughout.
Claude Code2026-06-18
Moving Cleanup and Logging into a SessionEnd Hook
How to use Claude Code's new post-session hook to automate temp-file cleanup and log writing after a session ends, with real examples from a pipeline that processes several repositories in sequence.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →