Files
supabase/apps/docs/data/monitoring-agents.utils.ts
T
Anthony Lio 4a9b4b0a9e feat(docs): o11y agent setup stepper (#50962)
## Problem

on monitoring agent pages the prompt lives in a "prompt" tab next to
agent ones, while each agent tab ended with "paste the prompt" users
have to work out that the prompt is sitting in that previous tab

## Solution

this pr is a proposal to set agent setup as a two stepper `1` for the
prompt panel `2` holds the agent tabs:

- extracts prompt into a first step
- moves agent tabs within their own step
- polishes agent docs to match recent ui updates
- sets `prompt` as an anchor link within agent tabs 

| state | preview |
| -------|------|
| before | <img width="823" height="452" alt="image"
src="https://github.com/user-attachments/assets/a6a01832-ea73-48c1-b31d-25a0ef970e4e"
/> |
| after | <img width="823" height="716" alt="image"
src="https://github.com/user-attachments/assets/f5014ad0-1555-4578-b4b1-5bf2733b4d37"
/> |

<!--
## Preview links

If relevant, include links to changed pages for easy review access.

Copy the preview base URL from the Vercel bot comment on this PR. Use
the following table as an example template.

| Site | Live | Preview | Search for |
| -------------- |
-------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------
| ----------------------------- |
| WWW | [/blog/your-post](https://supabase.com/blog/your-post) |
[/blog/your-post](https://zone-www-dot-com-git-branch-name-supabase.vercel.app/blog/your-post)
| unique phrase from the change |
| Docs |
[/docs/guides/your-page](https://supabase.com/docs/guides/your-page) |
[/docs/guides/your-page](https://docs-git-branch-name-supabase.vercel.app/docs/guides/your-page)
| unique phrase from the change |
| Studio | [/dashboard](https://supabase.com/dashboard) |
[/dashboard](https://studio-git-branch-name-supabase.vercel.app/dashboard)
| unique phrase from the change |
| Design system | [/design-system](https://supabase.com/design-system) |
[/design-system](https://design-system-git-branch-name-supabase.vercel.app/design-system)
| unique phrase from the change |
| UI library | [/library](https://supabase.com/library) |
[/library](https://ui-library-git-branch-name-supabase.vercel.app/library)
| unique phrase from the change |
| Knowledge base |
[/kb/guides/your-page](https://supabase.com/kb/guides/your-page) |
[/kb/guides/your-page](https://kb-git-branch-name-supabase.vercel.app/kb/guides/your-page)
| unique phrase from the change |
-->

<!-- ## Additional context

Optionally add any other context or screenshots.

-->

## Review instructions

Provide a clear numbered procedure that the PR reviewer can walk
through.

1. visit
[/automate-with-agents/health](https://docs-git-docs-agent-setup-stepper-supabase.vercel.app/docs/guides/observability/automate-with-agents/health#set-up-the-agent)

## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Agent setup instructions are organized into prompt-copying and
scheduling steps, with links to harness documentation.
  * Code tabs support icons and controlled selection.
  * Source code samples support adjustable footer notches.
* **Bug Fixes**
  * Step numbers now display the correct shadow.
* Links to page anchors now scroll to, focus, and highlight their
targets, while respecting reduced-motion preferences.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-28 17:28:11 +03:00

142 lines
4.8 KiB
TypeScript

import { aiPrompts } from './ai-prompts.data'
import {
monitoringAgents,
type MonitoringAgent,
type MonitoringAgentId,
} from './monitoring-agents.data'
export type MonitoringAgentHarnessKey = 'claude' | 'codex' | 'cursor'
export type ScheduleMark = {
key: string
label?: string
}
export type MonitoringAgentHarnessSetup = {
key: MonitoringAgentHarnessKey
label: string
icon: 'claude' | 'openai' | 'cursor'
hasDistinctDarkIcon?: boolean
docsUrl: string
intro: string
steps: string[]
note?: string
}
const WEEKDAY_LABELS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as const
const DAY_LABELS = ['12am', '6am', '12pm', '6pm'] as const
export const AGENT_PROMPT_ANCHOR = 'agent-prompt'
const MCP_STEP =
'Connect the [Supabase MCP server](/docs/guides/ai-tools/mcp) with `project_ref` and `read_only=true`.'
export function getMonitoringAgent(id: string): MonitoringAgent {
const agent = monitoringAgents[id as MonitoringAgentId]
if (!agent) {
throw new Error(`Unknown monitoring agent id: ${id}`)
}
return agent
}
export function getMonitoringAgentPrompt(agent: MonitoringAgent): string {
const prompt = aiPrompts[agent.promptId]
if (!prompt) {
throw new Error(`Unknown AiPrompt id: ${agent.promptId}`)
}
return prompt
}
export function getCronExpression(intervalMinutes: number): string {
if (intervalMinutes === 15) return '*/15 * * * *'
if (intervalMinutes === 60) return '0 * * * *'
if (intervalMinutes === 1440) return '0 9 * * *'
throw new Error(`Unsupported monitoring agent interval: ${intervalMinutes}`)
}
export function getScheduleMarks(intervalMinutes: number): {
window: 'day' | 'week'
marks: ScheduleMark[]
} {
if (intervalMinutes >= 24 * 60) {
return {
window: 'week',
marks: WEEKDAY_LABELS.map((label) => ({ key: label, label })),
}
}
const marksPerDay = (24 * 60) / intervalMinutes
if (!Number.isInteger(marksPerDay)) {
throw new Error(`intervalMinutes must divide 1440 evenly. Received: ${intervalMinutes}`)
}
const labelEvery = marksPerDay / 4
const marks = Array.from({ length: marksPerDay }, (_, index) => ({
key: String(index),
label: index % labelEvery === 0 ? DAY_LABELS[index / labelEvery] : undefined,
}))
return { window: 'day', marks }
}
export function getScheduleLabel(agent: MonitoringAgent): string {
const cadence = agent.schedule.cadence
return cadence.charAt(0).toUpperCase() + cadence.slice(1)
}
export function getMonitoringAgentHarnesses(agent: MonitoringAgent): MonitoringAgentHarnessSetup[] {
const cron = getCronExpression(agent.schedule.intervalMinutes)
const cadence = agent.schedule.cadence
const isSubHourly = agent.schedule.intervalMinutes < 60
return [
{
key: 'claude',
label: 'Claude',
icon: 'claude',
docsUrl: isSubHourly
? 'https://code.claude.com/docs/en/desktop-scheduled-tasks'
: 'https://code.claude.com/docs/en/routines',
intro: isSubHourly
? `Create a Claude Desktop scheduled task that runs ${agent.name} ${cadence}.`
: `Create a Claude routine that runs ${agent.name} ${cadence}.`,
steps: [
MCP_STEP,
isSubHourly
? 'In the Claude Code Desktop app, open **Routines**, click **New routine**, and choose **Local**.'
: 'Open [Claude routines](https://claude.ai/code/routines) or run `/schedule` in Claude Code.',
`Name it ${agent.name}. Paste the [prompt](#${AGENT_PROMPT_ANCHOR}). Set the schedule to ${cadence}.`,
],
note: isSubHourly
? 'Cloud routines have a 1-hour minimum. Use a [Desktop scheduled task](https://code.claude.com/docs/en/desktop-scheduled-tasks) for this cadence.'
: undefined,
},
{
key: 'codex',
label: 'Codex',
icon: 'openai',
hasDistinctDarkIcon: true,
docsUrl: 'https://developers.openai.com/codex/app/automations',
intro: `Create a Codex scheduled task that runs ${agent.name} ${cadence}.`,
steps: [
MCP_STEP,
'Open **Scheduled** in the ChatGPT desktop app, or ask Codex to create a standalone scheduled task.',
`Name it ${agent.name}. Paste the [prompt](#${AGENT_PROMPT_ANCHOR}). Set the schedule to ${cadence}. Each run should start a new chat.`,
],
},
{
key: 'cursor',
label: 'Cursor',
icon: 'cursor',
hasDistinctDarkIcon: true,
docsUrl: 'https://cursor.com/docs/cloud-agent/automations',
intro: `Create a Cursor automation that runs ${agent.name} ${cadence}.`,
steps: [
MCP_STEP,
'Create an automation in the Agents Window, at [cursor.com/automations](https://cursor.com/automations), or with the `/automate` skill.',
`Name it ${agent.name}. Use a scheduled trigger (${cadence}, cron \`${cron}\`). Paste the [prompt](#${AGENT_PROMPT_ANCHOR}). Keep the agent read-only, with no repository.`,
],
},
]
}