sourcepin
v0.2.0
Published
Pin feedback to UI and trace it to source for coding agents.
Maintainers
Readme
SourcePin
English | 简体中文
把反馈钉在 UI 上,直接追踪到源码。
SourcePin 是为编码 Agent 设计的本地优先可视化反馈层。你可以在运行中的 Vite 应用里点击元素并留下批注;SourcePin 会把路由、DOM 身份、组件和源码位置记录到仓库中。之后的 Codex 会话可以读取 pin、修改正确的代码,再把结果交还给人类验收。
点击 UI → 留下反馈 → Agent 修改源码 → 人类接受或重新打开无需云端账号。Pin 保存在 .sourcepin/ 下;你可以选择是否随项目提交。最终验收权始终属于人类。
在本地运行同一个交互 Demo:
pnpm install
pnpm demo打开 http://127.0.0.1:4190,点击 SourcePin 后选择模式:Element 可以点击创建持久化 pin,也可以拖出带源码定位的目标;Text 保留浏览器原生文字划选,并显示 Copy context / Drag to agent;Region 用矩形框选 viewport 区域,并导出最多 8 个相交的 DOM/源码候选。所有拖拽都会提供受限的 text/plain 和结构化 application/x-sourcepin,且不会自动提交目标输入。区域坐标仅作为 evidence,不会被当成持久身份。不同终端的支持程度可能不同,因此持久化 pin 和 Copy 仍是可靠 fallback。
0.2.0 新增内容
- 通过已验证的
next dev --webpack路径支持 Next.js App Router,使用项目本地 Route Handler,并在生产构建中移除 SourcePin。 - 可以把页面元素直接拖入兼容的编码 Agent 或终端文本输入目标。
- 原生文字划选支持 Copy context 和 Drag to agent。
- Region 框选会导出数量受限的相交 DOM/源码候选。
- 紧凑且互斥的 Element / Text / Region 控件,不会自动提交 Agent 输入。
跨会话工作仍以持久化 pin/MCP 流程为 canonical 路径;drag 和 copy 是给已打开 Agent 会话使用的快速交接方式。
为什么需要 SourcePin?
一张截图或一句“修一下这个按钮”会让 Agent 猜测目标。SourcePin 会提供结构化上下文:
- 问题出现时的路由和内部 UI Surface;
- 稳定 selector 与语义 fallback anchor;
- React/Vue 组件和项目相对源码位置;
- 原始批注、viewport 和元素几何信息;
- Agent 无法自行通过验收的受控状态。
SourcePin 适合定位交互、文案、布局和状态问题。截图只是可选证据,不是必需项,也不会自动触发视觉模型。
快速开始
1. 安装
pnpm add -D sourcepin2. 添加 Vite 插件
把 SourcePin 放在框架插件之前,使其能检查原始 JSX/TSX 或 Vue template。
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import sourcepin from "sourcepin/vite";
export default defineConfig({
plugins: [sourcepin(), react()],
});Vue 使用相同顺序:
plugins: [sourcepin(), vue()]3. 挂载开发 Overlay
// src/main.tsx、src/main.ts 或等价入口
import { mountSourcePin } from "sourcepin";
if (import.meta.env.DEV) {
void mountSourcePin();
}启动应用,点击紫色 SourcePin 按钮,选择元素并填写反馈。左侧 Feedback 按钮用于打开验收抽屉。
Pin 会以经过校验的 JSON 保存:
.sourcepin/pins/pin_<uuid>.jsonNext.js App Router
自
[email protected]起提供。
SourcePin 通过 webpack 开发路径支持 Next.js App Router。先配置 Next,为源码注入 metadata,并把浏览器协议 rewrite 到项目本地 Route Handler:
// next.config.mjs
import { withSourcePin } from "sourcepin/next";
export default withSourcePin({}, { root: process.cwd() });开发时使用 webpack:
{
"scripts": {
"dev": "next dev --webpack"
}
}添加一个 catch-all App Router handler。由于下划线开头的 App Router 目录会被视为 private folder,公开的 /__sourcepin/* 协议会 rewrite 到这里:
// app/sourcepin-api/[...sourcepin]/route.ts
import { createSourcePinRouteHandlers } from "sourcepin/next";
export const dynamic = "force-dynamic";
const handlers = createSourcePinRouteHandlers({ root: process.cwd() });
export const { GET, POST, PATCH } = handlers;只有服务端 layout 处于开发模式时才挂载 Client Overlay:
// app/sourcepin.tsx
"use client";
import { useEffect } from "react";
import { mountSourcePin } from "sourcepin";
export function SourcePinOverlay() {
useEffect(() => {
let dispose: undefined | (() => void);
void mountSourcePin().then((value) => { dispose = value; });
return () => dispose?.();
}, []);
return null;
}// app/layout.tsx(放在 <body> 内)
{process.env.NODE_ENV === "development" ? <SourcePinOverlay /> : null}当前 adapter 明确支持 next dev --webpack,不宣称支持 Turbopack。Package gate 会把 packed tarball 安装到独立 Next.js 16 App Router consumer,在 Chromium 中真实创建并刷新恢复 pin,通过 Route Handler 和仓库 store 读回数据,运行 next build --webpack,最后确认生产 HTML 既没有源码 metadata,也没有 SourcePin Overlay。
日常工作流
在浏览器中留下反馈
Overlay 提供:
- 元素 hover 与选择;
- 附着在实时目标上的编号 marker;
- 可收起的反馈抽屉;
Open、Review和Accepted筛选;- 跨路由导航和内部 tab 恢复;
- 仅供人类使用的 Accept 和 Reopen 操作。
为 Agent 安装仓库指令
在使用 SourcePin 的项目中安装内置 skill:
pnpm exec sourcepin init仅使用 CLI 的 Agent 可以发现并读取任务:
pnpm exec sourcepin next --json
pnpm exec sourcepin context pin_<id>修改代码并运行相关测试后,Agent 把 pin 交回人工验收:
pnpm exec sourcepin changed pin_<id> src/components/Card.tsx状态流刻意设计为不对称:
open ──Agent──▶ changed-awaiting-review ──人类──▶ accepted
▲ │ │
└────────────── 人类 reopen ◀───────────────────────────┘Agent 可以报告已经验证的修改,但只有人类可以接受它。
Codex MCP
SourcePin 内置 stdio MCP server。在使用 SourcePin 的仓库中注册:
codex mcp add sourcepin -- pnpm exec sourcepin-mcp验证注册结果:
codex mcp list可用工具:
| 工具 | 用途 | 修改 pin 状态 |
| --- | --- | --- |
| list_pins | 列出并筛选反馈 | 否 |
| get_context | 返回 pin、受限源码片段和 Surface metadata | 否 |
| mark_changed | 在修改通过验证后,把 open pin 移交人工验收 | 是 |
MCP 刻意不提供 accept 工具。只读工具声明了 MCP 安全 annotations,所有操作都被限制在当前仓库内。
目标恢复原理
SourcePin 从不持久化 HTMLElement。React/Vue 条件分支卸载、modal 关闭或虚拟列表回收行时,DOM 节点可能消失。因此,每个 pin 会保存多组相互独立的定位信号。
Selector 优先级:
data-testid
→ data-sourcepin-key
→ id
→ 注入的源码 metadata
→ 有界 DOM path首选 selector 漂移时,SourcePin 会根据元素 tag、文本、role、accessible name 和源码位置给候选目标评分。只有一个候选明确胜出时才会恢复 marker。
反馈抽屉会显示以下目标状态:
| 状态 | 含义 |
| --- | --- |
| attached | 当前唯一解析到一个可见目标 |
| dormant | 目标已隐藏或卸载,但对应 UI Surface 可以恢复 |
| unresolved | 当前没有可靠目标;反馈仍保留给 Agent |
| ambiguous | 多个候选同样合理,SourcePin 拒绝猜测 |
SourcePin 不会退回到旧屏幕坐标上错误绘制 marker。
Tab、Modal 和瞬态 UI
SourcePin 通过 role="tabpanel" 和对应的 role="tab" 检测标准 ARIA tabs。点击 dormant feedback card 可以重新激活记录的 tab,并再次解析目标。
自定义内部视图可以显式声明 Surface:
<section
data-sourcepin-surface="settings.billing"
data-sourcepin-surface-label="Billing"
>重复行如果具有相同文本和源码 metadata,可以增加稳定业务键:
<article data-sourcepin-key="user:42">持久化的 activation recipe 只允许 selector click 等结构化操作。Pin 文件不能携带任意 JavaScript。
框架支持
| Surface | 状态 |
| --- | --- |
| Vite middleware 与 overlay | 已支持 |
| React JSX/TSX 源码注入 | 已支持 |
| Vue SFC template 源码注入 | 已支持 |
| React/Vue 条件卸载和隐藏 DOM | 已支持 |
| ARIA tabs 与显式自定义 Surface | 已支持 |
| Next.js App Router(next dev --webpack) | 自 0.2.0 起支持;已通过独立项目 dogfood |
| Nuxt 原生与 SvelteKit adapter | 不在范围内 |
| 浏览器扩展 | 不在范围内;项目内集成能提供更可靠的源码映射 |
Vue 注入使用 Vue template AST,不会修改 <script> 字符串和组件标签。JSX/TSX 注入使用 Babel AST。遇到不支持的语法时,两种 transform 都会 fail open,不会阻塞开发构建。
存储与安全
SourcePin 为本地开发设计:
- Vite middleware 只会在
.sourcepin/pins/下写入经过校验的文件; - 每个仓库根目录都是独立 store,不同项目中的并行 Codex 会话无法看到彼此的 pin;
- 同一 pin 的更新使用项目本地原子锁和原子 rename,多个 Agent 无法同时完成同一次
open → changed-awaiting-review转换; - pin ID、路由、selector、几何信息、源码路径、Surface 和 evidence path 都会经过校验;
- request body 有大小限制;
- 读取源码上下文时会拒绝仓库外的 lexical escape 和 symlink escape;
- comment 被视为不可信反馈,而不是可执行的 Agent 指令;
- UI 文本不会通过不安全的 HTML 注入渲染;
- 最终验收始终由人类负责。
Middleware 不可用时,浏览器可以临时回退到 localStorage。Fallback key 包含稳定的 Vite 项目根目录 fingerprint,避免复用同一 localhost origin 的项目共享 fallback pin。跨会话 Agent 工作仍以仓库中的 pin 为 canonical 数据。
可选视觉证据
Pin 可以引用由单独 adapter 或 sidecar 生成的截图:
{
"evidence": {
"screenshots": [".sourcepin/assets/pin_example.png"]
}
}Evidence 最多包含 20 个位于 .sourcepin/assets/ 下的项目本地 PNG/JPEG/WebP 文件;该目录默认被忽略。SourcePin 不会自动截图,也不会自动调用视觉模型。
CLI 参考
sourcepin init
sourcepin list [--json]
sourcepin next [--json]
sourcepin show <pin_id>
sourcepin context <pin_id>
sourcepin changed <pin_id> <changed_files...>
sourcepin accept <pin_id>
sourcepin reopen <pin_id>Agent 会话使用 changed;accept 和 reopen 应保留给人工验收。
开发
要求 Node.js 20+ 和 pnpm 10.15.1。
pnpm install
pnpm exec playwright install chromium
pnpm gatepnpm gate 会运行:
- 严格 TypeScript 检查;
- 单元测试和协议测试;
- library build;
- Billing Demo build;
- packed tarball consumer smoke;
- 真实 stdio MCP handshake 和工具调用;
- Chromium 端到端测试,覆盖 Element/Text/Region 交接模式、持久化、滚动、路由、tab、隐藏/瞬态 DOM、selector drift 和浏览器验收操作。
GitHub Actions 会在 Linux 上运行同一套 gate。npm release 使用带 provenance 的 Trusted Publishing/OIDC。
项目状态
SourcePin 是一个早期公开版本,当前重点是验证从本地 UI 到源码的反馈闭环。核心 Vite、React/Vue、基于 webpack 的 Next.js App Router、CLI、MCP、验收、打包和 CI 路径均已实现并经过测试。目前不宣称支持 Turbopack。Nuxt 原生、SvelteKit 和浏览器扩展均不在项目范围内。Comment 编辑/删除和更强的源码 fingerprint 仍可作为未来工作。
