---
title: Summarize PRs in Slack
description: Every pull request lands in Slack with a reviewer-ready brief — what changed, why it matters, and where to look first. One job, two integrations, no bots to configure.
slug: pr-summaries-in-slack
group: engineering
trigger: github pr.opened
steps:
  - "slack: post heads-up in #engineering"
  - "agent: summarize the diff"
  - "slack: reply in thread"
integrations:
  - github
  - slack
outcome: Reviewers open every PR already knowing what changed, why, and where to start reading.
publishedAt: "2026-07-14"
updatedAt: "2026-07-14"
featured: true
---

Review latency is mostly orientation cost: before anyone comments, someone has
to work out what a PR is actually doing. This job pays that cost once, at open
time. The moment a pull request opens, it posts a heads-up in the team channel,
has an agent read the diff, and threads a summary under the heads-up — what
changed, why it matters, and what reviewers should look at first.

## How it works

1. **Trigger** — a pull request opens in the target repo; PRs from bots are
   filtered out before the job runs.
2. **Heads-up** — a deterministic tool call posts one line to the engineering
   channel: who opened what. Fixed channel, fixed format, no agent involved.
3. **Summary** — `generateText` runs with the GitHub skill scoped to the repo,
   so the agent can read the diff, the changed files, and recent commits.
4. **Thread reply** — the summary posts as a threaded reply under the
   heads-up, keeping the channel to one line per PR.

## Requirements

- A Terse project (`terse init`) with the **GitHub** and **Slack**
  integrations connected (`terse integrate connect github`, then `slack`).
- `terse generate` run after connecting, so `Repos.*` and `SlackChannel.*`
  exist as typed constants in `src/terse.generated.ts`.
- A repo to watch and a channel to post to — the only two decisions this job
  needs.

## The job

```typescript
import { createJob, generateText, type GithubPROpenedTrigger } from "terse-sdk";
import {
  Triggers,
  Skills,
  Repos,
  SlackChannel,
  toolbox,
} from "../terse.generated";

createJob({
  name: "Summarize PR and notify Slack",
  triggers: [Triggers.github.onPROpened({ repo: Repos.MyOrg.MyRepo })],
  filter: async (event: GithubPROpenedTrigger) => {
    return !event.sender.login.includes("[bot]");
  },
  onTrigger: async (event: GithubPROpenedTrigger) => {
    // Deterministic: fixed channel, fixed opener — use toolbox, no agent needed
    const message = await toolbox.slack.sendMessage({
      channelId: SlackChannel.Engineering.channelId,
      message: `New PR from ${event.sender.login}: ${event.pullRequest.title}`,
      thread_ts: "",
      blocks: "",
    });

    // Agentic: only the summary needs judgment
    const summary = await generateText({
      prompt:
        `Summarize the changes in this PR. ` +
        `Focus on what changed, why it matters, and what reviewers should look at first. ` +
        `Keep it concise. Context: ${event.formatForAgentRunner()}`,
      skills: [Skills.github({ repos: [Repos.MyOrg.MyRepo] })],
    });

    // Deterministic: post the result back in thread
    await toolbox.slack.sendMessage({
      channelId: SlackChannel.Engineering.channelId,
      message: summary,
      thread_ts: message.message_ts,
      blocks: "",
    });
  },
});
```

`Repos.MyOrg.MyRepo` and `SlackChannel.Engineering` aren't strings you look up
— they're typed constants generated from your connected workspace by
`terse generate`, so a renamed channel is a compile error, not a silent
misfire.

## Make it yours

- **Scope by path.** Only summarize PRs that touch `src/` or a critical
  directory — add the check to the `filter`.
- **Route by team.** Map `CODEOWNERS` entries to different channels and send
  each summary where its reviewers actually live.
- **Add risk flags.** Extend the prompt to call out migrations, dependency
  bumps, and auth-adjacent changes explicitly.
- **Cover follow-up pushes.** Add `Triggers.github.onPRSynchronized()` to
  re-summarize when new commits land on an open PR.

## For coding agents

This page is a build template. To implement it in a Terse project:

1. Satisfy the Requirements section above (init, connect, generate).
2. Create `src/jobs/pr-summaries-in-slack.ts` with the job above and add
   `import "./jobs/pr-summaries-in-slack"` to `src/terse.jobs.ts`.
3. The constants (`Repos.*`, `SlackChannel.*`) and trigger/skill factories must
   come from the user's own `src/terse.generated.ts` — verify every name there
   and substitute the user's real repo and channel. Never invent names.
4. Ask the user which repo and channel to target, test with
   `terse test run "Summarize PR and notify Slack"` against a pinned sample
   event, then deploy with `terse deploy` after they confirm.
