feedback-to-pr

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Feedback → Cursor Cloud Agent → PR

反馈 → Cursor Cloud Agent → PR

Wire a product feedback loop end-to-end:
floating control → DOM screenshot + fetch/console ring buffer → dialog →
POST /api/feedback
(row per user) → background job emails confirmation and launches a Cursor Cloud Agent (
autoCreatePr: true
) with the screenshot + logs → Cursor webhook marks
pr_open
and emails the PR link → GitHub webhook on merge marks
merged
and emails "shipped".
Status lifecycle:
submitted → agent_running → pr_open → merged
(or
failed
).
Implement this in the target app. Do not redesign the loop — the gotchas below are load-bearing.

搭建端到端的产品反馈流程:
悬浮控件 → DOM截图 + fetch/console环形缓冲区 → 对话框 →
POST /api/feedback
(每条用户反馈对应一条数据库记录)→ 后台任务发送确认邮件并 启动带有截图和日志的Cursor Cloud Agent(
autoCreatePr: true
)→ Cursor Webhook标记
pr_open
并发送PR链接邮件 → GitHub Webhook在合并时标记
merged
并发送“已发布”邮件。
状态生命周期:
submitted → agent_running → pr_open → merged
(或
failed
)。
在目标应用中实现此流程。请勿重新设计流程——以下注意事项是流程正常运行的关键。

Prerequisites

前置条件

  • Route handlers (Next.js App Router or equivalent)
  • A database that can store a feedback row (Postgres + any ORM is fine)
  • Session/auth that yields a user id + email
  • A background job runner with retries (Inngest, etc.)
  • Transactional email (Resend or equivalent)
  • Cursor Cloud Agents API access + GitHub App grant on the target repo
If the app has no organizations concept, omit org fields — everything else stands alone.

  • 路由处理器(Next.js App Router或同类工具)
  • 可存储反馈记录的数据库(Postgres + 任意ORM均可)
  • 可获取用户ID和邮箱的会话/认证系统
  • 支持重试的后台任务运行器(如Inngest等)
  • 事务邮件服务(如Resend或同类工具)
  • Cursor Cloud Agents API访问权限 + 目标仓库的GitHub App授权
如果应用没有组织概念,可省略组织字段——其余部分均可独立使用。

What to build

构建内容

PieceRole
Shared types
bug
|
feedback
; network/console log entry shapes
Network recorderPatch
fetch
+
console.error
into ring buffers. Strip query strings. Never record headers or bodies.
ScreenshotDOM capture (e.g.
modern-screenshot
). Optional — try/catch to
null
, never block submit.
WidgetClient provider + dialog; snapshot page on open;
POST /api/feedback
Submit routeAuth required; Zod-validate body; rate-limit (~5/hr); persist row; emit job event
{ feedbackId }
only
Feedback tablemessage, pageUrl, userAgent, screenshot (+ dims), networkLogs, consoleLogs, cursorAgentId, branchName, prUrl, status
Job(1) confirmation email, (2) create Cursor agent with prompt + screenshot, (3) store agent id →
agent_running
Cursor client
POST https://api.cursor.com/v0/agents
with
autoCreatePr: true
and a webhook URL (v0 still owns webhooks in the create body — check current Cursor docs before migrating to v1)
Cursor webhookVerify HMAC over raw body; on
FINISHED
+
prUrl
pr_open
+ email; on ERROR/CANCELLED or FINISHED without PR →
failed
GitHub webhook
pull_request
closed+merged → match by
prUrl
(fallback: branch) →
merged
+ email
HMAC helperSHA-256 hex, optional
sha256=
prefix,
timingSafeEqual
Adaptation points: the app's session helper, design-system dialog/button, Cursor prompt path hint ("work in
apps/web
"), schema migrate, job registration.

组件作用
共享类型
bug
|
feedback
;网络/控制台日志条目结构
网络记录器
fetch
console.error
进行补丁,将数据存入环形缓冲区。移除查询字符串。绝不记录请求头或请求体。
截图功能DOM捕获(如
modern-screenshot
)。可选功能——通过try/catch捕获异常并返回
null
,绝不阻止提交操作。
小部件客户端提供者 + 对话框;打开时捕获页面快照;调用
POST /api/feedback
接口
提交路由需要认证;使用Zod验证请求体;速率限制(约5次/小时);持久化记录;仅发送包含
{ feedbackId }
的任务事件
反馈数据表包含message、pageUrl、userAgent、screenshot(含尺寸)、networkLogs、consoleLogs、cursorAgentId、branchName、prUrl、status字段
后台任务(1) 发送确认邮件,(2) 使用提示词和截图创建Cursor Agent,(3) 存储Agent ID → 将状态标记为
agent_running
Cursor客户端调用
POST https://api.cursor.com/v0/agents
接口,携带
autoCreatePr: true
和Webhook URL(v0版本仍需在创建请求体中指定Webhook——迁移到v1前请查阅Cursor最新文档)
Cursor Webhook基于原始请求体验证HMAC签名;当状态为
FINISHED
且存在
prUrl
时 → 标记为
pr_open
并发送邮件;当状态为ERROR/CANCELLED或FINISHED但无PR时 → 标记为
failed
GitHub Webhook
pull_request
关闭且合并时 → 通过
prUrl
匹配反馈记录(备选:通过分支名匹配)→ 标记为
merged
并发送邮件
HMAC工具类SHA-256十六进制编码,可选前缀
sha256=
,使用
timingSafeEqual
验证
适配点:应用的会话工具、设计系统的对话框/按钮、Cursor提示词路径提示(如“在
apps/web
目录中工作”)、数据库 schema 迁移、任务注册。

Env vars

环境变量

VarNotes
CURSOR_API_KEY
Cursor Dashboard → API Keys
CURSOR_REPO_URL
https://github.com/<org>/<repo>
the agent should edit
CURSOR_WEBHOOK_SECRET
openssl rand -hex 24
(≥ 32 chars)
GITHUB_WEBHOOK_SECRET
invent it; same value in the GitHub webhook form
RESEND_API_KEY
/
RESEND_FROM_EMAIL
verified domain
WEBHOOK_PUBLIC_URL
public tunnel in dev; falls back to app public URL in prod
App public URL (e.g.
NEXT_PUBLIC_APP_URL
)
browser-facing origin — not the tunnel

变量说明
CURSOR_API_KEY
Cursor控制台 → API密钥
CURSOR_REPO_URL
Agent需编辑的仓库地址:
https://github.com/<org>/<repo>
CURSOR_WEBHOOK_SECRET
通过
openssl rand -hex 24
生成(长度≥32字符)
GITHUB_WEBHOOK_SECRET
自行生成;需与GitHub Webhook配置中的值一致
RESEND_API_KEY
/
RESEND_FROM_EMAIL
已验证的域名
WEBHOOK_PUBLIC_URL
开发环境使用公网隧道;生产环境回退到应用公网URL
应用公网URL(如
NEXT_PUBLIC_APP_URL
浏览器可访问的域名 —— 请勿使用隧道地址

One-time external setup

一次性外部配置

  1. Cursor ↔ GitHub — grant the Cursor GitHub App on the repo. Verify with
    GET https://api.cursor.com/v0/repositories
    .
  2. GitHub webhook
    ${WEBHOOK_PUBLIC_URL}/api/webhooks/github
    , secret =
    GITHUB_WEBHOOK_SECRET
    , events: Pull requests only.
  3. Dev tunnel — HTTPS tunnel to the local app port →
    WEBHOOK_PUBLIC_URL
    . Run the job runner alongside the app.

  1. Cursor ↔ GitHub — 为仓库授权Cursor GitHub App。通过调用
    GET https://api.cursor.com/v0/repositories
    验证权限。
  2. GitHub Webhook — 地址为
    ${WEBHOOK_PUBLIC_URL}/api/webhooks/github
    ,密钥为
    GITHUB_WEBHOOK_SECRET
    ,仅监听Pull requests事件。
  3. 开发环境隧道 — 将本地应用端口通过HTTPS隧道映射到
    WEBHOOK_PUBLIC_URL
    。需与应用同时运行任务运行器。

Hard-won gotchas

经验总结的注意事项

  1. Never point the auth/public app URL at the tunnel. Cross-origin auth dies with "Failed to fetch". That is what
    WEBHOOK_PUBLIC_URL
    is for.
  2. Cursor's "Failed to verify existence of branch 'main'" means no repo access, not a wrong branch. Fix the GitHub App grant.
  3. Confirmation email is the FIRST job step, before agent creation — otherwise a Cursor failure means the user never hears anything.
  4. Job event carries only
    { feedbackId }
    .
    Payloads/step returns are size-capped (~512KB); the screenshot stays in the DB and is re-read in the step.
  5. Webhook signatures verify over the raw body (
    await req.text()
    before parsing). Status-guarded UPDATEs (
    WHERE status IN (...)
    + returning) make replays no-ops — only email when a row actually transitioned.
  6. Cloud agents appear at cursor.com/agents, not in the IDE agent list.
  7. Screenshot is optional end-to-end (iframes/cross-origin images blank out).
  8. Swallow email failures in webhook handlers after the DB write, or the provider redelivers forever over a mail blip.
  9. Cursor
    FINISHED
    without
    prUrl
    → mark
    failed
    , don't leave
    agent_running
    .

  1. 切勿将认证/应用公网URL指向隧道地址。 跨域认证会因“Failed to fetch”失败。
    WEBHOOK_PUBLIC_URL
    才是用于此场景的地址。
  2. Cursor提示“Failed to verify existence of branch 'main'”意味着无仓库访问权限,而非分支名称错误。需修复GitHub App授权。
  3. 确认邮件是任务的第一步,需在创建Agent之前发送——否则若Cursor失败,用户将收不到任何通知。
  4. 任务事件仅携带
    { feedbackId }
    负载/步骤返回结果有大小限制(约512KB);截图需存储在数据库中,在步骤中重新读取。
  5. Webhook签名需基于原始请求体验证(解析前先执行
    await req.text()
    )。基于状态的更新操作(
    WHERE status IN (...)
    + 返回结果)可确保重放请求无副作用——仅当记录状态实际变更时才发送邮件。
  6. 云Agent显示在cursor.com/agents,而非IDE的Agent列表中。
  7. 截图在整个流程中是可选的(iframe/跨域图片会显示为空)。
  8. 在Webhook处理器中,数据库写入完成后需忽略邮件发送失败,否则邮件服务会因短暂故障而持续重发。
  9. 若Cursor状态为
    FINISHED
    但无
    prUrl
    → 标记为
    failed
    ,不要停留在
    agent_running
    状态。

Verification

验证步骤

  1. Typecheck/lint clean; job runner reports the new function.
  2. Submit feedback → DB row, confirmation email, Cursor agent RUNNING.
  3. Simulate webhooks if not tunneled: signed payloads →
    pr_open
    merged
    ; replay idempotent; bad signature → 401; closed-without-merge → no-op.
  4. Negatives: unauthenticated → 401; over rate limit → 429.
See
references/architecture.md
for the status machine diagram.
  1. 类型检查/代码扫描无错误;任务运行器能识别新增的任务函数。
  2. 提交反馈 → 生成数据库记录、发送确认邮件、Cursor Agent处于RUNNING状态。
  3. 若未使用隧道,可模拟Webhook:签名的负载 → 标记为
    pr_open
    → 标记为
    merged
    ;重放请求具有幂等性;无效签名返回401;未合并的关闭PR不触发任何操作。
  4. 异常场景验证:未认证请求返回401;超过速率限制返回429。
状态机图请查看
references/architecture.md