HarborHarbor
DocumentationGuidesPlugins
Recipes

Daily Digest

Scheduled Function posts a daily roll-up of your workspace activity to Slack.

A scheduled Function that runs at 9 AM every weekday, pulls open work across Linear, Sentry, and GitHub, and posts a single condensed summary to Slack. Saves your team's morning standup.

daily-digest.ts

defineJob({
  name: "daily-digest",
  description: "Morning summary of open work to #standup.",
  handler: async (ctx) => {
    const [issues, errors, prs] = await Promise.all([
      ctx.plugins.linearMcp.listIssues({
        filter: { state: { type: { in: ["unstarted", "started"] } } },
        limit:  20,
      }),
      ctx.plugins.sentryMcp.listIssues({
        status: "unresolved",
        limit:  10,
      }),
      ctx.plugins.githubMcp.searchIssues({
        q: "repo:acme/api is:pr is:open draft:false",
      }),
    ]);

    const summary = await ctx.orbit.ai.run({
      model: "anthropic/claude-3.7-sonnet",
      prompt: [
        "Write a tight morning standup roll-up in 6-8 lines. Lead with",
        "what blocks work; lump similar items.",
        `Open Linear: ${issues.length}`,
        `Unresolved Sentry: ${errors.length}`,
        `Open PRs: ${prs.length}`,
        "Top Linear titles: " + issues.slice(0, 5).map((i) => i.title).join("; "),
        "Sentry titles: "    + errors.slice(0, 5).map((e) => e.title).join("; "),
      ].join("\n"),
      maxTokens: 300,
    });

    await ctx.plugins.slackMcp.postMessage({
      channel: "#standup",
      text:    `*Morning roll-up*\n\n${summary}`,
    });

    return { issues: issues.length, errors: errors.length, prs: prs.length };
  },
});
hrbr inspect -f ./daily-digest.job.ts
hrbr inspect 'return await hrbr.triggers.list({ limit: 20 })'

Create or review the weekday schedule in the Harbor control plane; trigger runs appear in the same trace history as manual invocations.

Cost note

Each run = 3 plugin calls + 1 orbit.ai.run. The AI call is the bulk of the cost — pin the model and watch the tokens in Billing → Usage.

Variations

  • Per-channel topics: filter Linear by team label, post each team's digest to their own channel.
  • PR review reminders: extra Slack DM to PR authors whose PRs are

    24h old without review.

  • Pause on holidays: skip with if (isHoliday(new Date())) return; at the top.