Skip to content
← Public packages

@kentcdodds/sentry-triage

Sentry triage wakes Cole (Grok Bot) per issue: one active wake per repo (lease + queue), loop-safe Discord. Cole may spawn Cursor for isolated repo work.

src/format-discord-report.ts

111 lines · 3.3 KB · TypeScript
import { formatKentReport } from 'kody:@kentcdodds/discord/format-report'
// Rebundle official v3 application emoji mentions from format-report.
import postMessage from 'kody:@kentcdodds/discord/post-message'
import editMessage from 'kody:@kentcdodds/discord/edit-message'
import { formatReportDecisionFields } from './decision-fields.ts'
import { readTriageSettings } from './settings.ts'

export type SentryReportInput = {
	status: string
	title?: string | null
	shortId?: string | null
	issueId?: string | null
	projectSlug?: string | null
	link?: string | null
	summary?: string | null
	prUrl?: string | null
	prUrls?: string[]
	agentUrl?: string | null
	mergeCommitUrl?: string | null
	deployUrl?: string | null
	activityUrl?: string | null
	extras?: string[]
	context?: string | null
	recommendation?: string | null
	rightFix?: string | null
	whyNotRightFix?: string | null
	options?: Array<string | { label?: string; text?: string; difficulty?: string; change?: string }> | string | null
}

function pushUniqueLink(
	links: Array<{ label: string; url: string }>,
	label: string,
	url: string | null | undefined,
) {
	const trimmed = typeof url === 'string' ? url.trim() : ''
	if (!trimmed) return
	if (links.some((link) => link.url === trimmed && link.label === label)) return
	links.push({ label, url: trimmed })
}

export function formatSentryReport(input: SentryReportInput) {
	const decision = formatReportDecisionFields(input)
	const facts = []
	if (input.projectSlug) facts.push({ label: 'Project', value: input.projectSlug })
	const links = []
	pushUniqueLink(links, 'Issue', input.link)
	pushUniqueLink(links, 'Activity', input.activityUrl)
	if (input.agentUrl === 'grok-bot:cole') {
		facts.push({ label: 'Owner', value: 'Cole (Grok Bot)' })
	} else {
		pushUniqueLink(links, 'Agent', input.agentUrl)
	}
	pushUniqueLink(links, 'PR', input.prUrl)
	for (const url of input.prUrls || []) {
		pushUniqueLink(links, 'PR', url)
	}
	pushUniqueLink(links, 'Merge', input.mergeCommitUrl)
	pushUniqueLink(links, 'Deploy', input.deployUrl)
	return formatKentReport({
		kind: 'sentry',
		title:
			decision.title ||
			String(input.title || 'Sentry issue').trim() ||
			'Sentry issue',
		status: input.status,
		id: input.shortId || input.issueId || undefined,
		facts,
		context: decision.context,
		recommendation: decision.recommendation,
		rightFix: decision.rightFix,
		whyNotRightFix: decision.whyNotRightFix,
		options: decision.options,
		summary: decision.summary,
		links,
		extras: input.extras,
	})
}

export async function postSentryReport(input: SentryReportInput) {
	const formatted = formatSentryReport(input)
	// 429 retries live in @kentcdodds/discord/post-message (honors retry_after).
	return postMessage({
		channelId: (await readTriageSettings()).discordChannelId,
		content: formatted.content,
		flags: formatted.flags,
	})
}

export async function editSentryReport(
	messageId: string,
	input: SentryReportInput,
) {
	const formatted = formatSentryReport(input)
	return editMessage({
		channelId: (await readTriageSettings()).discordChannelId,
		messageId,
		content: formatted.content,
		flags: formatted.flags,
	})
}

export async function upsertSentryReport(
	messageId: string | null | undefined,
	input: SentryReportInput,
) {
	if (messageId) {
		await editSentryReport(messageId, input).catch(() => {})
		return { id: messageId }
	}
	return postSentryReport(input)
}