关于 tyrekick
Tyrekick 为原型页面提供访客反馈组件,让审阅者直接在页面元素上留言,供编码 Agent 获取并处理。反馈带有元素文本、标题和页面错误等上下文,处理后可标记为已解决。
主要功能
- 01在页面元素上固定评论
- 02收集带上下文的反馈
- 03通过 MCP 获取并处理评论
- 04解决评论并更新标记
- 05评论抽屉与草稿恢复
- 06支持自有存储端点
适用场景
- 01收集原型页面的审阅意见
- 02让编码 Agent 根据反馈修复问题
- 03查看页面错误及对应反馈
- 04将评论发送到自有存储端点
MCP 相关
Tyrekick 的 MCP 服务提供列出、获取、分类、解决反馈及统计工具。编码 Agent 可从配置的 Worker 获取评论并将其标记为已解决。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y tyrekick-mcpTYREKICK_URLBase URL of your deployed Tyrekick feedback worker (e.g. https://tyrekick-feedback.you.workers.dev)TYREKICK_TOKEN密钥Bearer token for the worker's management routes (set via `wrangler secret put TYREKICK_TOKEN`)本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
6收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | integer | 否 | 返回条目数上限(worker 默认值为 50,最大值为 200) |
| route | string | 否 | 仅返回此 route 上留下的反馈 |
| since | string | 否 | 仅返回在此 ISO-8601 时间戳当日或之后创建的反馈 |
| status | enum | 否 | 仅返回具有此 status 的反馈(open = 尚未分类)open | approved | declined | resolved |
| project | string | 否 | 仅返回此 project_name 的反馈(一个 worker 可服务多个项目) |
详细介绍
Tyrekick
发布前先试试。 邀请你信任的人评审由 AI 构建的原型,再让你的编码 Agent 根据他们的反馈采取行动。
评审者可直接在实时页面上固定评论,无需账户,也无需培训。你的编码 Agent 通过 MCP 获取这些评论,并附上对应的确切元素、可见文本、所在部分的标题以及任何页面错误;它会修复标记的问题,并将其标记为已解决。评审者的标记会变绿,并附上备注。你再也不必把反馈重新输入提示词了。

▶ 试用在线演示 · 快速入门 · 文档 · “外层循环”——为何要打造它
真实演示:这条评论固定在实时演示页面上,由 Agent 修复后,标记变绿——无需重新输入反馈、无需跟踪工具、无需账户。
工作原理
reviewer's browser destination YOU own your coding agent
┌──────────────────┐ POST ┌────────────────────┐ MCP ┌─────────────────┐
│ pin a comment on │ ────────► │ Discord webhook or │ ◄───────► │ "fix the open │
│ the live page │ │ your CF Worker │ │ feedback" │
└──────────────────┘ └────────────────────┘ └─────────────────┘
这个小组件是一个仅约 14 KB(gzip 压缩后)的脚本,零依赖,并在 Shadow DOM 中渲染,不会与你的页面产生冲突。Tyrekick 没有后端,也没有账户,更不会向任何地方发送数据——反馈会直接从评审者的浏览器 POST 到你拥有的目的地。它旨在满足这样的需求:由 Agent 构建的原型需要更多人来审视。你不必把截图贴进群聊后眼看回复淹没其中,每条评论都会以结构化、带版本信息的形式送达,并固定在确切位置。
发表评论后,标记会留在页面上(在你与之交互前会变暗)。悬停在标记上可查看评论,点击即可就地打开讨论串,并在标记处显示回复、重试和丢弃操作。评审者还可以使用评论抽屉(右侧的概览列表,列出每个标记及其评论文本——点击即可跳回对应位置,也可以完全隐藏标记)、恢复草稿,以及完整的键盘和触摸操作支持(按 Cmd/Ctrl+Enter 发送)。启用 persist: true(默认值)时,localStorage 仅用于在重新加载后恢复尚未发送的内容——未能送达的评论会在重新加载后恢复,并带有“重试”和“丢弃”按钮;即使窗口大小发生变化,评论也会重新固定到对应元素上。
Agent: 要将 Tyrekick 安装到项目中?使用
make-reviewable技能——只需一句话,它就会在需要时托管页面、选择目的地、部署 worker、安装小组件、连接 MCP,并起草要发送给评审者的邀请;也可以按照AGENTS.md逐步操作。
完整文档位于 docs/ 中:快速入门 · 开始使用 · 配置 · 评审者体验 · 目的地 · Agent 工作流 · 载荷参考 · 故障排查与常见问题
要公开分享原型? 请先阅读公开原型,然后查看各项功能指南:速率限制 · 共享评审 · AI 自动回复。
60 秒快速入门(Discord)
最快的方式是使用一个由你管理的 Discord 频道——先体验一下反馈流向你掌控的位置。完整产品(持久化、状态,以及你的 Agent 通过 MCP 完成反馈闭环)见下文的工作器目标端。
一条命令(在项目文件夹中运行):
npx tyrekick init
命令会提示你输入 webhook URL,注入脚本标签,并发送一条测试评论。也可以手动操作:
- 在 Discord 中,打开服务器设置 → 集成 → Webhook → 新建 Webhook,选择一个频道,然后点击复制 Webhook URL。
- 将该 URL 粘贴到下面的代码片段中,并将其放在原型中
</body>之前:
<script
src="https://cdn.jsdelivr.net/npm/tyrekick@latest/dist/tyrekick.js"
data-webhook="https://discord.com/api/webhooks/XXXX/YYYY"
data-app-version="v0.1"
data-transport="discord"
data-project-name="My Prototype"
data-accent="#4f46e5"
data-position="bottom-right"
data-branding="true"
></script>
IIFE 构建会在 DOMContentLoaded 时,根据自身 <script> 标签的 data-* 属性自动初始化。评审者会在角落看到“提供反馈”按钮;每条评论都会以易读的消息形式发送到你的 Discord 频道。
只有
data-webhook和data-app-version是必填项。其他配置均为可选,并使用配置表中的默认值。
自托管 / 自有存储
Discord 是最省事的默认选项,但如果你希望将数据存储在自己掌控的存储空间中,请使用指向持久化该载荷的端点的 JSON 传输方式(transport: "json",默认值)。
- Cloudflare 模板(TypeScript): 请参阅
destinations/cloudflare,其中提供了可部署的 Worker,用于接收 v2 载荷、验证载荷,并将其写入你掌控的存储空间。由于代码由你掌控,这里也很适合添加垃圾信息验证。 - 同源 Pages Function: 如果你的原型托管在 Cloudflare Pages 上,可在同一来源部署 Pages Function(例如
/api/feedback),并将webhook设为该相对 URL 或同源 URL。同源意味着完全无需配置 CORS。
任何启用了 CORS 的端点或表单后端也都可以使用——采用 transport: "json" 时,成功条件为 HTTP 2xx;如果返回了响应正文,则不能是 {"ok":false}。
Agent 工作流(MCP)
这就是 Tyrekick 存在的意义。使用 Cloudflare Worker 目标端后,反馈不只是被收集起来——你的编码 Agent 还可以拉取反馈、采取行动并解决问题:
- 部署 Worker 并设置令牌(参见
destinations/cloudflare):wrangler deploy && wrangler secret put TYREKICK_TOKEN - 将工作流交给你的 Agent(只需一次——此后每次会话都适用):
claude mcp add tyrekick \ --env TYREKICK_URL=https://tyrekick-feedback.YOUR.workers.dev \ --env TYREKICK_TOKEN=your-token \ -- npx tyrekick-mcp - 用一句话完成闭环:
“列出未处理的反馈,修复大家指出的问题,然后将其标记为已解决。”
Agent 可以使用五种工具——list_feedback、get_feedback、triage_feedback、resolve_feedback、feedback_stats——每条反馈都包含元素的可见文本(可在源码中用 grep 查找)、最近的标题、路由、视口,以及任何未捕获的页面错误。像 “这个按钮点了没反应” 这样的评论会以如下形式收到:
element: <button> "Search trips"
under: "Plan your escape" · landmark: main > section#planner
page_errors: 1
> This button does nothing when I click it
这比大多数人写的 Bug 报告都更好。完整详情见 mcp/README.md。
共享审阅(审阅者可以看到彼此的评论)
默认情况下,审阅由多个私密对话组成:每位审阅者只能看到自己的标记,评论会发送到你的目标位置。设置审阅密钥后,页面本身就会成为共享空间——所有人都能看到彼此的标记,且只能查看,并附有署名。这样审阅者就不用把同一件事重复报告四遍了。
npx wrangler secret put TYREKICK_REVIEW_KEY # on your worker; any long random string
<script
src="https://cdn.jsdelivr.net/npm/tyrekick@latest/dist/tyrekick.js"
data-webhook="https://your-worker.workers.dev/feedback"
data-app-version="v0.1"
data-project-name="my-prototype"
data-review-key="the-same-long-random-string"
></script>
启用前请先阅读。 密钥会随页面一起发布,因此持有审阅链接的任何人都能看到它:任何能打开原型的人都能阅读其上的所有评论,包括审阅者姓名。 对于发送给五位可信人士的私密链接,这是合适的取舍;对于公开 URL,则不合适。没有按审阅者身份进一步限定访问范围的方式——审阅者永远无需登录,这正是设计初衷。轮换密钥即可撤销访问权限。
它的功能与限制:
- 只读。 你可以查看并回复其他审阅者的评论;回复会像其他评论一样发送到你的目标位置,而非审阅者之间直接互通。没有实时在线状态,也没有审阅者之间的页面内评论串。
- 拒绝即隐藏。 你拒绝的评论(通过 MCP 或 API)会从所有人的页面消失——这就是清除垃圾和无关内容的方式。评论作者仍可在自己的标记上看到处理结果。
- 按项目隔离。 一个 worker 可以服务多个原型;审阅密钥只会读取指定的
projectName。请设置明确且稳定的projectName——如果依赖document.title回退值,第一次修改标题时,你的反馈流就会被拆分。 - 仅限 Worker。 Discord 按设计仅支持写入,没有共享视图。
- **其他审阅者永远看不到的内容:**你的 User-Agent、屏幕或设备指纹、原型的页面错误、完整 URL(共享链接可能包含查询字符串密钥),或你的会话 ID。
AI 自动回复(可选)
在 worker 上设置 Anthropic API 密钥后,每条接收的评论都会收到一条简短、友好的确认回复——标记为 🤖,且只显示在审阅者自己的评论串中——同时标记保持打开状态(它只会确认收到;绝不会声称已修复任何问题,也不会解决标记)。模型没有工具,因此评论中的任何内容都无法让它执行操作。默认关闭。
npx wrangler secret put ANTHROPIC_API_KEY
编程式 / ESM 用法
从你的注册表安装并显式初始化(ESM 构建不会自动初始化):
npm install tyrekick
import { init, destroy } from "tyrekick";
init({
webhook: "https://discord.com/api/webhooks/XXXX/YYYY",
appVersion: "v0.1",
projectName: "My Prototype",
transport: "discord",
});
// later, to tear down all DOM, listeners, and in-memory state:
destroy();
全局(IIFE)等效调用为 window.Tyrekick.init(config) / window.Tyrekick.destroy()。未先调用 destroy() 而连续调用两次 init() 不会执行任何操作,并会发出警告;缺少 webhook 或 appVersion 会抛出错误。
框架 / 异步加载(Next.js、React、打包工具)
IIFE 构建会通过 document.currentScript 读取自身的 <script> 标签并自动初始化。只要标签是异步注入的(Next.js next/script、由 React 渲染的 <script>,或任何动态加载器),该值就会是 null,因此普通 CDN 标签可能会悄然无法启动。支持以下两种方式:
-
设置全局变量(适用于任何加载器)。在 widget 脚本运行前定义:
<script>window.tyrekickConfig = { webhook: "…", appVersion: "1.0" }</script>IIFE 构建会依次读取
window.tyrekickConfig、自身的data-*,然后回退到 DOM 中任意已有的<script data-webhook>。 -
从客户端组件调用
init()(推荐用于 Next.js App Router / React):"use client"; import { useEffect } from "react"; export function TyrekickWidget() { useEffect(() => { let cleanup: (() => void) | undefined; import("tyrekick").then(({ init, destroy }) => { init({ webhook: "…", projectName: "…", appVersion: "…" }); cleanup = destroy; }); return () => cleanup?.(); }, []); return null; }
配置
TyrekickConfig 的所有字段(参见 src/types.ts):
| 字段 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
webhook | string | 是 | — | 接收 POST 请求的目标 URL。 |
appVersion | string | 是 | — | 正在审核的原型版本字符串。 |
projectName | string | 否 | document.title | 项目的易读名称。 |
position | "bottom-right" | "bottom-left" | 否 | "bottom-right" | 触发按钮所在角落。 |
accent | string | 否 | "#FFC53D" | 触发器、标记和发送按钮的强调色。强调色上的文本会自动调整对比度(浅色强调色使用深色文字,深色强调色使用白色)。 |
theme | "auto" | "light" | "dark" | 否 | "auto" | 小组件的配色方案。"auto" 会在初始化时遵循访客的 prefers-color-scheme,并在其变化时实时更新。对于自动初始化构建,请在 script 标签上使用 data-theme。 |
branding | boolean | 否 | true | 显示“由 Frontier Operations 打造”页脚。 |
fields | { name?: boolean } | 否 | { name: true } | 切换可选的审核者姓名输入框。 |
transport | "json" | "discord" | 否 | "json" | 负载的传递方式(原始 JSON 或 Discord 消息)。 |
persist | boolean | 否 | true | 仅使用 localStorage 进行未发送内容的恢复:草稿文本和发送失败、重载后仍需重试的评论。已提交的评论不会保存在 localStorage 中。设为 false 时,不会读取或写入任何存储键。 |
reviewKey | string | 否 | — | 共享审核(可选启用):每位审核者都能看到其他所有审核者的标记,但只能查看。必须与 Worker 上的 TYREKICK_REVIEW_KEY 匹配。该密钥会随页面一起发布,因此任何拥有审核链接的人都能读取所有评论——适用于私密链接,不适用于公开 URL。参见共享审核(审核者可相互查看)。 |
captureErrors | boolean | 否 | true | 记录页面未捕获的错误 / 未处理的拒绝(通过 window 的 "error" 和 "unhandledrejection" 监听器——不会修改控制台),并将最近的 ≤5 条附加到每个负载中,作为 page_errors。对于自动初始化构建,请在 script 标签上设置 data-capture-errors="false"。无论此标志为何值,任何地方都不会捕获输入的值。 |
负载架构(v2)
使用 transport: "json" 时,POST 到你的 webhook 的原始请求体格式恰好如下(FeedbackPayload):
{
"schema": 2,
"id": "b1c2...",
"created_at": "2026-07-02T10:15:30.000+12:00",
"project_name": "My Prototype",
"app_version": "v0.1",
"route": "/pricing?ref=x#plans",
"url": "https://example.com/pricing?ref=x#plans",
"body": "This button is hard to find.",
"reviewer_name": "Sam",
"session_id": "9f8e...",
"anchor": {
"x_pct": 42.3,
"y_pct": 71.8,
"selector": "main > section.pricing > button.cta",
"viewport": { "w": 1440, "h": 900 },
"element": {
"tag": "button",
"id": "buy-now",
"testid": "pricing-cta",
"role": null,
"text": "Start free trial",
"label": "Start your free trial",
"rect": { "x": 612, "y": 484, "w": 216, "h": 44 }
},
"context": {
"heading": "Pricing plans",
"landmark": "main > section#pricing"
}
},
"env": {
"user_agent": "Mozilla/5.0 ...",
"language": "en-US",
"screen": { "w": 1512, "h": 982 },
"dpr": 2,
"dark": false,
"touch": false
},
"page_errors": ["TypeError: prices is undefined"]
}
字段说明:
schema始终为2。id和session_id均为 UUID(id对应每条评论,session_id在每次页面加载时生成一次)。created_at为带时区的 ISO-8601 格式。route为location.pathname + search + hash;url为location.href。body是去除首尾空白的评论文本;reviewer_name为string | null。anchor.x_pct/anchor.y_pct是点击时相对于文档尺寸的百分比,保留 1 位小数。anchor.selector是点击位置所在宿主页面最深层元素的尽力匹配 CSS 选择器(最多 5 段),也可能为null——绝不会指向工具自身的节点。anchor.element用于标识被点击的元素,方便编码 Agent 将反馈映射回源代码:小写的tag、id/data-testid(testid)/role属性(或null)、可见的text(去除首尾空白、合并空白,≤80 个字符;为空时为null)、label(aria-label→alt→placeholder→title,≤80 个字符),以及点击时相对于视口的边界rect(整数 px)。无法解析元素时为null。**隐私:**对于input/textarea/select,text始终为null——绝不会捕获表单值。anchor.context以结构化方式标明点击位置:heading是最近标题的文本(h1–h6,沿祖先节点及其前置兄弟节点向上查找,≤80 个字符);landmark是最多包含 3 个地标祖先的粗略路径(main、nav、header、footer、aside、section、article、form),最内层在最后,例如"main > section#pricing"。未找到时两者均为null;采集过程绝不会抛出异常。env会添加物理screen尺寸(CSS px)、dpr(devicePixelRatio,保留 2 位小数)、dark(prefers-color-scheme: dark)和touch(pointer: coarse)。page_errors是本次页面加载中最近发生的 ≤5 个未捕获错误 / 未处理的 Promise 拒绝(每条 ≤200 个字符,按时间从旧到新排列),通过window监听器收集——不会修改 console。若没有错误或captureErrors: false,则为[]。
使用 transport: "discord" 时,此负载会改为格式化为易读的 Discord 消息({ content }),并将其 POST。
限制
这是一个有意设计为零后端的工具,因此也存在一些取舍:
- 回执只会在 worker 路径上形成闭环。 使用 Cloudflare 目标时,解决评论会将审阅者的标记变为绿色,并附上解决说明;Discord 为只写入,不支持回读。
- 默认情况下,评审内容按浏览器隔离。 两位审阅者各自只能看到自己的标记,看不到对方的;评论会汇总到目标位置,而不是显示在页面上。共享评审 可在 worker 路径上启用另一种行为,但代价是任何拥有链接的人都能读取所有评论。
- 没有截图或会话回放。 只会发送上述 JSON 负载——不含图片、不采集 DOM、不录制内容。结构化的元素 / 上下文采集是有意采用的替代方案。
- 垃圾信息。 公开的 webhook 可能收到垃圾内容——这是无需后端和把关机制所要付出的代价。可以将
transport: "json"指向 Cloudflare 模板,并在那里添加验证 / 速率限制;也可以发送到只有审阅者能够访问的私有 Discord 频道。请参阅 docs/destinations 中基于托管方式的决策表。
贡献
npm run build # build dist/tyrekick.js (IIFE) + dist/tyrekick.esm.js (ESM)
npm test # unit tests (vitest)
npm run e2e # end-to-end tests (playwright)
请将 src/types.ts 保留为唯一事实来源——未经架构版本升级,不要重命名负载键或配置字段。
**重新渲染本 README 顶部的演示 GIF:**运行 node scripts/record-demo.mjs,即可在一次性本地 worker 上录制完整流程并重新编码。请参阅 scripts/README.md——录制过程由 scripts/record-demo.mjs 中的脚本控制(可调整)。
由 Frontier Operations 打造
Tyrekick 是 Frontier Operations 的品牌载体:这是一款真正实用的开源工具,让收集原型反馈变得毫无阻碍,同时践行我们看重的一项原则——评审者的评论绝不会经由我们传递。整个流程中没有 Frontier 后端,也不会有任何数据回传;反馈会从评审者的浏览器直接发送到你掌控的目的地。这是一款免费提供的实用软件,也在不经意间展现了我们的构建理念。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。