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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ yarn-error.log*
apps/**/public/build
.tests-container-id.txt
/app/routes/_marketing-site.components

# Misc
CLAUDE.md
ARCHITECTURE.md
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ For information on how to run each project, see the README in each directory.
| [Building effective agents](/building-effective-agents) | 5 different patterns for building effective AI agents with Trigger.dev; [Prompt chaining](/building-effective-agents/src/trigger/trigger/translate-copy.ts), [Routing](/building-effective-agents/src/trigger/trigger/routing-questions.ts), [Parallelization](/building-effective-agents/src/trigger/trigger/parallel-llm-calls.ts), [Orchestrator-workers](/building-effective-agents/src/trigger/trigger/orchestrator-workers.ts) |
| [Claude thinking chatbot](/claude-thinking-chatbot) | A chatbot that uses Claude's thinking capabilities to generate responses |
| [Claude agent SDK](/claude-agent-sdk-trigger) | A simple example of how to use the [Claude Agent SDK](https://docs.claude.com/en/docs/agent-sdk/overview) with Trigger.dev |
| [Claude changelog generator](/changelog-generator) | Generate changelogs from a GitHub repository using the [Claude Agent SDK](https://docs.claude.com/en/docs/agent-sdk/overview) with Trigger.dev |
| [Claude agent GitHub wiki](/claude-agent-github-wiki) | AI-powered repository analyzer that lets you ask questions about any public GitHub repository using Anthropic's [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), with real-time streaming via [Trigger.dev Realtime](https://trigger.dev/docs/realtime/overview) |
| [Deep research agent using the AI SDK](/vercel-ai-sdk-deep-research-agent/) | An intelligent deep research agent using the Vercel [AI SDK](https://sdk.vercel.ai/docs/introduction) and Trigger.dev |
| [Monorepos](/monorepos) | Examples of using Trigger.dev in monorepo setups with [Turborepo](https://turbo.build/) and [Prisma](https://www.prisma.io/) |
Expand Down
9 changes: 9 additions & 0 deletions changelog-generator/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Trigger.dev - get from https://cloud.trigger.dev
TRIGGER_SECRET_KEY=tr_dev_xxx
TRIGGER_PROJECT_REF=proj_xxx

# Anthropic - get from https://console.anthropic.com
ANTHROPIC_API_KEY=sk-ant-xxx

# GitHub (optional) - for private repos, needs `repo` scope
GITHUB_TOKEN=ghp_xxx
3 changes: 3 additions & 0 deletions changelog-generator/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
62 changes: 62 additions & 0 deletions changelog-generator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

CLAUDE.md

postgres-data
# dependencies
node_modules
.pnp
.pnp.js

# testing
coverage

# next.js
.next/
out/
dist
packages/**/dist

# Tailwind
apps/**/styles/tailwind.css
packages/**/styles/tailwind.css

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.docker
.docker/*.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# turbo
.turbo
.vercel
.cache
.env
.output
apps/**/public/build
.tests-container-id.txt
.sentryclirc
.buildt

**/tmp/
/test-results/
/playwright-report/
/playwright/.cache/

.cosine
.trigger
.tshy*
.yarn
*.tsbuildinfo
.claude
61 changes: 61 additions & 0 deletions changelog-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Changelog generator using the Claude Agent SDK and Trigger.dev

An AI agent that explores GitHub commits, investigates unclear changes by fetching diffs on demand, and generates developer-friendly changelogs. Built with the Claude Agent SDK and Trigger.dev.

## Tech Stack

- **[Next.js](https://nextjs.org)** – Frontend framework using App Router
- **[Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk)** – Anthropic's agent SDK for building AI agents with custom tools
- **[Trigger.dev](https://trigger.dev)** – Background task orchestration with real-time streaming to the frontend, observability, and deployment.
- **[Octokit](https://github.com/octokit/octokit.js)** – GitHub API client for fetching commits and diffs.

## Demo

<video src="https://content.trigger.dev/claude-changelog-generator.mp4" controls autoplay loop muted width="100%"></video>

## Running the project locally

1. **Install dependencies**

```bash
npm install
```

2. **Configure environment variables**

```bash
cp .env.example .env
```

- `TRIGGER_SECRET_KEY` – From [Trigger.dev dashboard](https://cloud.trigger.dev/)
- `TRIGGER_PROJECT_REF` – Your project ref (starts with `proj_`)
- `ANTHROPIC_API_KEY` – From [Anthropic Console](https://console.anthropic.com/)
- `GITHUB_TOKEN` (optional) – For private repos, needs `repo` scope

3. **Start development servers**

```bash
# Terminal 1: Next.js
npm run dev

# Terminal 2: Trigger.dev
npx trigger.dev@latest dev
```

4. Open [http://localhost:3000](http://localhost:3000) in your browser to see the demo

## Features

- **Two-phase analysis** – Lists all commits first, then selectively fetches diffs only for ambiguous ones to minimize token usage
- **Custom MCP tools** – `list_commits` and `get_commit_diff` called autonomously by Claude
- **Real-time streaming** – Changelog streams to the frontend as it's generated via Trigger.dev Realtime
- **Live observability** – Agent phase, turn count, and tool calls broadcast via run metadata
- **Markdown rendering** – Streamed output formatted with [Streamdown](https://github.com/vercel/streamdown) and Shiki syntax highlighting
- **Private repo support** – Optional GitHub token for private repositories

## Relevant Files

- [trigger/generate-changelog.ts](trigger/generate-changelog.ts) – Main task with MCP tools
- [trigger/changelog-stream.ts](trigger/changelog-stream.ts) – Stream definition
- [app/api/generate-changelog/route.ts](app/api/generate-changelog/route.ts) – API endpoint
- [app/response/[runId]/page.tsx](app/response/[runId]/page.tsx) – Streaming display page
51 changes: 51 additions & 0 deletions changelog-generator/app/api/generate-changelog/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { tasks } from "@trigger.dev/sdk";
import type { generateChangelog } from "@/trigger/generate-changelog";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
try {
const { repoUrl, startDate, endDate } = await request.json();

// Validate inputs
if (!repoUrl || typeof repoUrl !== "string") {
return NextResponse.json(
{ error: "Repository URL is required" },
{ status: 400 }
);
}

if (!startDate || !endDate) {
return NextResponse.json(
{ error: "Start and end dates are required" },
{ status: 400 }
);
}

// Basic GitHub URL validation
const githubUrlPattern = /^https?:\/\/(www\.)?github\.com\/[\w-]+\/[\w.-]+/;
if (!githubUrlPattern.test(repoUrl)) {
return NextResponse.json(
{ error: "Invalid GitHub URL format" },
{ status: 400 }
);
}

// Trigger the changelog task
const handle = await tasks.trigger<typeof generateChangelog>(
"generate-changelog",
{ repoUrl, startDate, endDate }
);

return NextResponse.json({
runId: handle.id,
accessToken: handle.publicAccessToken,
});
} catch (error: unknown) {
console.error("Failed to trigger generate-changelog task:", error);
const message = error instanceof Error ? error.message : "Failed to start changelog generation";
return NextResponse.json(
{ error: message },
{ status: 500 }
);
}
}
83 changes: 83 additions & 0 deletions changelog-generator/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

/* GitHub Dark Mode Theme */
@layer base {
:root {
color-scheme: dark;

/* GitHub dark: #0d1117 */
--background: 215 28% 7%;
/* GitHub text: #e6edf3 */
--foreground: 210 40% 92%;

/* GitHub elevated: #161b22 */
--card: 215 24% 11%;
--card-foreground: 210 40% 92%;

--popover: 215 24% 11%;
--popover-foreground: 210 40% 92%;

/* GitHub green: #238636 */
--primary: 132 69% 36%;
--primary-foreground: 0 0% 100%;

/* GitHub subtle: #21262d */
--secondary: 215 19% 15%;
--secondary-foreground: 210 40% 92%;

/* GitHub muted bg: #21262d */
--muted: 215 19% 15%;
/* GitHub muted text: #7d8590 */
--muted-foreground: 215 8% 53%;

--accent: 215 19% 15%;
--accent-foreground: 210 40% 92%;

/* GitHub red: #da3633 */
--destructive: 1 68% 53%;
--destructive-foreground: 0 0% 100%;

/* GitHub border: #30363d */
--border: 212 14% 21%;
--input: 212 14% 21%;

/* GitHub blue: #58a6ff */
--ring: 212 100% 67%;

--chart-1: 132 69% 36%;
--chart-2: 212 100% 67%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;

--radius: 0.5rem;
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

/* Inline code styling */
:not(pre) > code {
@apply bg-secondary px-1.5 py-0.5 rounded font-mono text-sm;
}

/* Date input calendar icon */
input[type="date"] {
position: relative;
}

input[type="date"]::-webkit-calendar-picker-indicator {
position: absolute;
right: 12px;
filter: invert(0.5);
cursor: pointer;
}
23 changes: 23 additions & 0 deletions changelog-generator/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
title: "Changelog Generator - AI-powered changelogs from GitHub commits",
description:
"Generate developer-friendly changelogs from any GitHub repository using AI",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
Loading