关于 swain-search
swain-search 是一项研究 Skill,用于收集、规范化并缓存来源,形成可复用的资料集。它支持创建、扩展、刷新和发现资料集,并对远程来源执行证据核验。
主要功能
- 01创建、扩展、刷新和发现资料集
- 02规范化网页、文档及媒体来源
- 03检查现有资料集中的相关研究
- 04记录并核验远程来源快照
适用场景
- 01为研究主题收集来源
- 02向现有资料集添加新材料
- 03按主题查找已收集的来源
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cristoslc/ swain --skill swain-search安装后在 Claude Code 中输入/swain-search确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CHANGELOG.mdREADME.mdreferences/manifest-schema.mdnormalization-formats.mdpaywall-proxies.yamltrovewatch-guide.mdwordlist.txtscripts/bootstrap.shconvert-cookies.pyexport-snapshot.shextract_frames.pyfetch_x_thread.pylog-snapshot-metadata.shmigrate-to-troves.shocr_frames.pyparse_vtt.pyresolve-proxy.shtrovewatch.shverify-snapshot-evidence.shyt-dlp.shtests/test-convert-cookies.pytest-export-snapshot.shtest-resolve-proxy.sh详细介绍
swain-search
收集、规范化并缓存源资料,形成可供 swain-design 产物引用的可复用资料库。
脚本调用约定
脚本位于此技能的 scripts/ 目录下。使用占位符 <SKILL_DIR> 表示包含此 SKILL.md 的文件夹,并在运行时解析。在已安装的技能中,该目录为 .claude/skills/swain-search/。在 swain 仓库中,该目录为 skills/swain-search/。
在每个会话中,运行媒体或 X 线程流程之前,先运行一次引导脚本:
bash "<SKILL_DIR>/scripts/bootstrap.sh"
该脚本会检查 uv 是否位于 PATH 中。首次运行后,~/.local/share/swain-search/.bootstrapped 中的标记文件会使后续运行直接跳过初始化。如果脚本以非零状态退出,请停止并告知操作者缺少什么。
模式检测
| 信号 | 模式 |
|---|---|
| 该主题没有资料库,或用户说“研究 X”/“收集资料” | Create — 新建资料库 |
| 资料库已存在,且用户提供了新来源或说“添加到”/“扩充” | Extend — 向现有资料库添加来源 |
| 资料库已存在,且用户说“刷新”或来源已超过 TTL | Refresh — 重新获取过期来源 |
| 用户询问“我们有哪些资料库”或“查找关于 X 的来源” | Discover — 按标签搜索现有资料库 |
既有资料检查
在创建新资料库或执行网页搜索之前,先扫描现有资料库中的相关内容。这样可以避免重复研究,并发现与既有工作的关联。
阶段 1 — 字面关键词匹配
搜索来源名称、URL 片段和作者姓名:
# Search trove manifests by tag
grep -rl "<keyword>" docs/troves/*/manifest.yaml 2>/dev/null
# Search trove source content
grep -rl "<keyword>" docs/troves/*/sources/**/*.md 2>/dev/null
# Search trove syntheses
grep -rl "<keyword>" docs/troves/*/synthesis.md 2>/dev/null
阶段 2 — 语义主题匹配
获取来源并了解其内容后,从来源的内容(而不仅是名称或 URL)中提取 3-5 个主题关键词。然后按主题搜索现有资料库:
# Search trove tags for topic keywords
grep -l "<topic-keyword-1>\|<topic-keyword-2>\|<topic-keyword-3>" docs/troves/*/manifest.yaml 2>/dev/null
# Search synthesis summaries for topic keywords
grep -l "<topic-keyword-1>\|<topic-keyword-2>\|<topic-keyword-3>" docs/troves/*/synthesis.md 2>/dev/null
主题关键词应描述来源的主题,而不是它的名称。例如,一个名为 “Cog” 的仓库实现了 Claude Code 的记忆系统,其主题关键词应为 agent-memory、memory-architecture、claude-code、persistent-memory 等,而不是 cog 或 marciopuga。
如果尚未获取来源(仅提供 URL),请使用 URL 或标题中可用的主题信息,并在获取来源后再进行完整的主题匹配。
决策关卡
在继续进入 Create 或 Extend 模式之前,输出一条可见的路由决策:
既有资料检查: 阶段 1 找到 [N 个匹配项/未找到匹配项]。阶段 2 找到 [N 个匹配项/未找到匹配项]:[trove-id(标签:x、y),…]。 决策: 扩充 [trove-id]/新建资料库 [slug],因为 [原因]。
这样可以审计资料库路由决策。如果任一资料库匹配 2 个或更多主题关键词,默认进入 Extend 模式,除非该主题确实不同(主题相近但内容不同)。
匹配项的处理方式
如果现有资料库包含相关来源:
- 报告发现内容 — 显示资料库 ID、匹配的来源标题及相关摘录
- 建议扩充而非新建 — 如果现有资料库涵盖相同主题,应扩充该资料库,而不是另建平行资料库
- 交叉链接 — 如果主题相近但内容不同,则创建新资料库,并在 synthesis.md 中注明相关资料库
所有模式(Create、Extend、Discover)都会执行此步骤,并且在任何网页搜索之前执行。始终优先检查现有资料库内容。
快照证据门禁(SPEC-220)
在远程来源被视为已收集的证据之前,本次运行必须生成原始快照,并在 .agents/search-snapshots/metadata.jsonl 中记录一条元数据。
远程来源的必要流程:
- 首先导出/下载原始快照:
bash "<SKILL_DIR>/scripts/export-snapshot.sh" --url "<source-url>" --out-dir ".agents/search-snapshots/raw"
- 使用
writing-skills或skill-creator规范化下载的文件(绝不能只保留浏览器摘要笔记)。 - 记录元数据:
bash "<SKILL_DIR>/scripts/log-snapshot-metadata.sh" --source-url "<source-url>" --export-mode "<mode>" --raw-path "<raw-path>" --normalized-path "<normalized-path>" --normalization-skill "<writing-skills|skill-creator>"
- 发布前进行验证:
bash "<SKILL_DIR>/scripts/verify-snapshot-evidence.sh" --source-url "<source-url>"
如果验证失败,请将该来源标记为未经验证,不要将其发布到下游,并向操作人员报告警告。
创建模式
从零开始构建一个资料库。
步骤 1 — 收集输入
向用户询问(或根据上下文推断)以下信息:
- 资料库 ID — 主题的短标识(例如
websocket-vs-sse)。如果上下文明确,请给出建议。 - 标签 — 用于发现的关键词(例如
real-time、websocket、sse) - 来源 — 可任意组合以下类型:
- 网络搜索查询(“搜索 WebSocket 与 SSE 的对比”)
- URL(网页、论坛帖子、文档)
- 视频/音频 URL
- 本地文件路径
- 新鲜度 TTL 覆盖项 — 可选;大多数资料库使用默认值即可
如果此流程由 swain-design 调用(例如,某个 spike 进入 Active 阶段),工件上下文会提供主题、标签,有时也会提供初始来源。
步骤 2 — 收集并规范化
针对每个来源,使用相应的功能。阅读 references/normalization-formats.md,了解每种来源类型对应的确切 Markdown 结构。
网络搜索查询:
- 使用网络搜索功能查找相关结果
- 选择最相关的前 3–5 个结果
- 对每个结果:抓取页面,并按照网页格式规范化为 Markdown
- 如果没有可用的网络搜索功能,请告知用户并跳过
网页 URL:
- 使用浏览器或页面抓取功能获取页面
- 删除样板内容(导航栏、广告、侧边栏、Cookie 横幅)
- 按照网页格式规范化为 Markdown
- 如果抓取失败,请在清单中记录该 URL,并设置
failed: true标记,然后继续
需要身份验证的网站(支持 Cookie):
当目标网站需要身份验证或订阅权限时,请提供从浏览器导出的 Cookie:
-
从已登录目标网站的浏览器中导出 Cookie:
- Firefox:使用 “cookies.txt” 扩展,或通过 DevTools → Storage → Cookies → 右键单击 → “Export All” 导出。
- Chrome:使用 “EditThisCookie” 扩展,或通过 DevTools → Application → Cookies → 右键单击 → “Export” 导出。
- 也可以使用任何能够生成 JSON 数组的工具,其中 Cookie 对象包含
Host raw、Name raw、Content raw、Path raw、Expires raw、Send for raw和This domain only raw字段。
-
将导出的 JSON 文件传递给
export-snapshot.sh:bash "<SKILL_DIR>/scripts/export-snapshot.sh" \ --url "<source-url>" \ --out-dir ".agents/search-snapshots/raw" \ --cookies "path/to/cookies.json"--cookies标志会通过convert-cookies.py将 Cookie 转换为 Netscape 格式,并将 Cookie jar 附加到 curl 请求中。导出模式会记录为<mode>-with-cookies。 -
如果未提供 Cookie,则在不进行身份验证的情况下继续抓取(与之前的行为相同)。
-
对于基于浏览器的页面抓取路径(MCP 浏览器工具),可自动使用浏览器自身会话中的 Cookie,无需提供单独的 Cookie 文件。此标志适用于基于 curl 的快照导出。 类似 Google Docs / Drive 的文档:
-
先导出原始内容(必需):
bash "<SKILL_DIR>/scripts/export-snapshot.sh" --url "<source-url>" --out-dir ".agents/search-snapshots/raw"
-
优先使用 API 导出模式(
google-doc-export、google-slides-export、google-drive-download)。 -
如果 API 导出失败,仅在浏览器辅助工具可用时使用回退方案。
-
使用
writing-skills或skill-creator规范化导出的文件。 -
在
.agents/search-snapshots/metadata.jsonl中记录元数据。 -
在将来源纳入 trove 输出之前,使用
verify-snapshot-evidence.sh进行验证。
付费墙代理回退方案:
获取网页后,检查该 URL 的域名是否有可用的付费墙代理:
- 运行
bash "<SKILL_DIR>/scripts/resolve-proxy.sh" <url>- 退出代码 1:未配置代理——直接使用获取到的内容
- 退出代码 0:输出
PROXY:<name>:<proxy-url>和SIGNAL:<text>行
- 如果退出代码为 0,检查获取到的内容中是否包含每个
SIGNAL文本(区分大小写的字面匹配) - 如果任一信号匹配(或文章正文不足约 200 个词):
- 记录:"检测到
<url>有付费墙——尝试代理回退方案" - 按顺序尝试每个
PROXYURL,使用与网页相同的页面获取功能抓取内容 - 首个返回实质性内容(长于被截断的原始内容)的代理胜出
- 在清单条目中设置
proxy-used: <name>和notes: "Full article retrieved via <name> proxy"
- 记录:"检测到
- 如果没有信号匹配:直接使用获取到的内容(无需代理)
- 如果所有代理均失败:保留原始截断内容,并设置
notes: "Paywalled; proxies exhausted — content from direct fetch only"
代理注册表位于 references/paywall-proxies.yaml。在此处添加新域名或代理——无需修改技能文件。
视频/音频 URL(YouTube、Instagram、播客):
遵循以下分层流程。每一层都会写入 /tmp/swain_search_media_transcript.txt。随后根据 references/normalization-formats.md 中的媒体格式规范化该文件。输出写入 sources/<source-id>/<source-id>.md。
-
获取字幕和元数据,通过一次 yt-dlp 调用完成:
bash "<SKILL_DIR>/scripts/yt-dlp.sh" --write-auto-sub --sub-lang en --write-info-json --skip-download --sub-format vtt -o "/tmp/swain_search_media" "<URL>"对于 Instagram URL,添加
--cookies-from-browser <browser>(chrome/safari/firefox/brave)。对于尚未发布在 YouTube 上的播客或会议 URL,先通过网页搜索功能将标题解析为 YouTube 链接。 -
VTT 路径(优先)。如果
/tmp/swain_search_media.en.vtt存在且非空:test -s /tmp/swain_search_media.en.vtt && uv run "<SKILL_DIR>/scripts/parse_vtt.py"脚本会写入
[HH:MM:SS] line片段。在来源 frontmatter 中设置transcript-source: vtt。 -
字幕说明回退方案。如果没有 VTT,则读取
/tmp/swain_search_media.info.json并获取description字段。移除#\w+标签。去除首尾空白。如果剩余文本超过 100 个字符,则将其写入/tmp/swain_search_media_transcript.txt(每行一个段落)。设置transcript-source: caption。省略时间戳。 -
帧提取回退方案(需要操作员批准)。如果说明文字过短(不超过 100 个字符),则停止并询问:
未找到字幕或可用的说明文字。我可以提取帧并读取屏幕上的文字来生成文字稿。这需要
opencv-python-headless(约 30MB,通过 uv 安装)。是否继续?获得批准后:
- 下载视频:
bash "<SKILL_DIR>/scripts/yt-dlp.sh" -o "/tmp/swain_search_video.mp4" "<URL>"。对于 Instagram,添加--cookies-from-browser <browser>。 - 提取帧:
uv run --with opencv-python-headless python3 "<SKILL_DIR>/scripts/extract_frames.py" /tmp/swain_search_video.mp4。通过场景变化检测保存/tmp/swain_search_frame_000.png及后续帧。 - 探测视觉读取能力。使用 Read 工具读取
/tmp/swain_search_frame_000.png。尝试读取可见文字。如果成功,继续执行第 5 步;如果失败,继续执行第 6 步。
- 下载视频:
-
视觉 OCR(首选)。对每个剩余帧使用 Read 工具。提取所有可见文本。去重相邻帧中的重复内容。将文本写入
/tmp/swain_search_media_transcript.txt。设置transcript-source: vision-ocr。不添加时间戳。 -
本地 OCR 回退方案。如果视觉方案失败,请询问:
视觉方案不可用。将通过 EasyOCR 回退到本地 OCR(首次运行需下载约 400 MB)。是否继续?
获得批准后:
uv run --with "easyocr,opencv-python-headless" python3 "<SKILL_DIR>/scripts/ocr_frames.py"设置
transcript-source: local-ocr。不添加时间戳。 -
规范化并写入来源。根据视频标题生成来源 ID slug。仅使用小写字母、数字和连字符。按照媒体格式将内容写入
sources/<source-id>/<source-id>.md。在 frontmatter 中添加transcript-source。仅当第 2 步已运行时,才添加duration、speakers和 YouTube 深层链接。
如果所有层级均未成功,则在清单中记录该来源,并设置 failed: true 和 reason: <tier>。
X/Twitter 讨论串:
URL 模式:(x|twitter|fxtwitter|fixupx).com/.+/status/\d+。通过公开的 fxtwitter API 展开。无需身份验证。
-
获取讨论串:
uv run "<SKILL_DIR>/scripts/fetch_x_thread.py" "<URL>"脚本会写入两个文件:
/tmp/swain_search_thread.json保存原始响应,/tmp/swain_search_thread_transcript.txt保存拼接后的文本,其中包含带引文的帖子块引用。脚本还会向 stdout 输出一个元数据 JSON 对象。提取以下字段:author_name、author_handle、author_url、published_date、tweet_count、title_guess、source_url、post_urls、cited_posts。 -
无法展开的讨论串。如果脚本在讨论串开篇帖子上退出,并显示 "only 1 post returned",说明上游部署缺少账户代理。将该条目记录为
failed: true和reason: x-thread-unrollable。继续处理。 -
将来源 ID 生成为
<author_handle>-<first-few-title-words>。规范化为仅包含小写字母、数字和连字符。去除所有@。 -
按照
references/normalization-formats.md中的 x-thread 格式进行规范化:- Frontmatter:在通用字段中添加
author-handle、author-name、published-date和tweet-count。 - 正文:将每条帖子原文呈现为编号列表。将每个编号链接回对应的推文 URL。去除开头自动添加的提及链。这些是 X 在回复中添加的
@handle前缀。将行内@mentions链接为[@handle](https://x.com/handle)。将话题标签链接为[#tag](https://x.com/hashtag/tag)。 - 引用帖子:将每个
cited_posts条目作为缩进块引用,置于引用它的帖子下方。最多追加 3 条有实质内容的自回复,作为续文。跳过只有裸 URL 的自回复;这些内容已出现在external_links中。若自回复超过 3 条,则添加链接。
- Frontmatter:在通用字段中添加
-
保存到
sources/<source-id>/<source-id>.md。
本地文件:
- 使用文档转换功能(PDF、DOCX 等),或在文件已是 Markdown 格式时直接读取
- 使用
writing-skills或skill-creator,按照文档格式进行规范化 - 对于 Markdown 文件:仅添加 frontmatter,保留内容
论坛讨论串 / 讨论:
- 获取内容,并按照论坛格式进行规范化(按时间顺序排列,标注作者)
- 将嵌套讨论串展平为按时间顺序排列的内容,并保留回复对象上下文
代码仓库:
- 克隆代码仓库或读取其内容
- 在
sources/<source-id>/下镜像原始目录树 - 默认镜像整个目录树。对于大型代码仓库(数千个文件),选择性摄取内容,并在清单条目中设置
selective: true - 将最重要文件的路径填入
highlights数组(相对于 source-id 目录)
文档站点:
- 抓取或获取文档站点
- 在
sources/<source-id>/下镜像章节层级 - 默认镜像整个站点。对于大型站点,选择性摄取内容,并设置
selective: true - 将最重要页面的路径填入
highlights数组 - 尽可能保留内部链接结构
CLI 工具: 首先,检测目标是否为 CLI 工具。检查以下条件:
- 目标存在于
PATH中(运行command -v <tool-name>) - 名称符合 CLI 模式(小写字母、连字符、不含空格)
- 上下文表明这是一个命令行工具
如果目标是 CLI 工具,请运行以下采集流程:
-
手册页采集:
- 运行
man <tool-name> - 如果成功,保存为
cli-manpage类型 - 如果没有手册页,则跳到帮助信息采集
- 运行
-
主要帮助信息采集:
- 运行
<tool-name> --help - 如果失败,尝试
<tool-name> -h - 保存为
cli-help类型
- 运行
-
子命令发现:
- 在帮助输出中查找以下模式:
- "Commands:" 或 "Subcommands:" 标题
- "Usage:" 部分下缩进排列的命令列表
<tool> <command> [options]之类的命令模式
- 过滤掉非命令项:
- 保留单个单词或带连字符的字符串
- 保留用法行中的位置参数
- 跳过以
-开头的内容(这些是标志)
- 在帮助输出中查找以下模式:
-
递归采集子命令:
- 对每个子命令,运行
<tool-name> <subcommand> --help - 保存为
cli-subcommand-help类型,并设置depth: 1 - 如果子命令还有自己的子命令,则再深入一层
- 最大深度:2 层
- 对每个子命令,运行
-
失败处理:
- 没有手册页?仅采集帮助信息
--help和-h都失败?在清单中注明- 所有采集均失败?标记为
failed: true并继续
每次采集都将成为一个独立的来源:
sources/<tool>-manpage/<tool>-manpage.md(类型:cli-manpage)sources/<tool>-help/<tool>-help.md(类型:cli-help)sources/<tool>-<subcommand>-help/<tool>-<subcommand>-help.md(类型:cli-subcommand-help)
每个规范化来源都会获得一个基于 slug 的来源 ID,并采用每个来源单独存放在目录中的布局:
- 扁平来源(web、forum、media、document、local):
sources/<source-id>/<source-id>.md - 分层来源(repository、documentation-site):
sources/<source-id>/,其中镜像保留原始目录树
来源 ID 生成:
- 根据来源标题或 URL 派生 slug 作为来源 ID(例如:
mdn-websocket-api、strangeloop-2025-realtime) - 如果 slug 与现有来源 ID 冲突,则使用
references/wordlist.txt中的两个随机单词追加__word1-word2 - 如果词表缺失,则追加
__和 4 位十六进制字符作为备用方案(例如:__a3f8)
步骤 3 — 生成清单
按照 references/manifest-schema.md 中的架构创建 manifest.yaml。包括:
- 知识库元数据(id、创建日期、标签)
- 每种来源类型的默认新鲜度 TTL
- 每个来源各有一个条目,包含来源信息(URL/路径、获取日期、内容哈希、类型)
将内容哈希计算为规范化 Markdown 内容的纯十六进制 SHA-256 摘要(不带前缀):
shasum -a 256 sources/mdn-websocket-api/mdn-websocket-api.md | cut -d' ' -f1
步骤 4 — 生成综合总结
创建 synthesis.md,对所有来源的关键发现进行结构化提炼。
按主题而非来源组织综合总结。将相关发现分组,按 ID 引用来源,并呈现:
- 关键发现 — 所有来源对该主题的综合结论
- 一致之处 — 各来源结论趋同的地方
- 分歧之处 — 来源相互矛盾或提出不同方案的地方
- 空白 — 来源未涉及但可能重要的内容
保持简洁。综合总结是一个起点,而非全面报告;用户或产物作者会对其进行完善。
第 5 步 — 提交并标记
使用双提交模式(与 swain-design 生命周期标记相同),为资料集生成一个可访问的提交哈希。
提交 A 之前 — 在 manifest.yaml 中添加一条 history 记录,并用 -- 占位表示提交哈希:
history:
- event: created
date: 2026-03-09
commit: "--"
sources: 3
提交 A — 提交资料集内容:
git add docs/troves/<trove-id>/
git commit -m "research(<trove-id>): create trove with N sources
Co-Authored-By: <model-name-from-system-prompt> <noreply@unknown>"
TROVE_HASH=$(git rev-parse HEAD)
提交 B — 将提交哈希回填到历史记录中,然后更新引用此资料集的工件的 frontmatter(如果存在):
# Replace "--" with the real hash in the history entry
# Update artifact frontmatter: trove: <trove-id>@<TROVE_HASH>
git add docs/troves/<trove-id>/manifest.yaml
git add docs/<artifact-type>/<phase>/<artifact-dir>/ # if artifact exists
git commit -m "docs(<trove-id>): stamp history hash ${TROVE_HASH:0:7}
Co-Authored-By: <model-name-from-system-prompt> <noreply@unknown>"
如果还没有引用此资料集的工件(独立研究),提交 B 仍需标记历史记录 — 报告该哈希,以便之后引用。
推送 — 提交 B 后,始终推送到 origin/trunk,以便其他 Agent 和会话立即使用此资料集。这是强制要求,不可选:
git push origin trunk
生成综合文件 URL — 为最终报告构建指向综合文件的稳定永久链接:
REMOTE_URL=$(git remote get-url origin | sed 's/\.git$//' | sed 's/[email protected]:/https:\/\/github.com\//')
SYNTHESIS_URL="${REMOTE_URL}/blob/${TROVE_HASH}/docs/troves/<trove-id>/synthesis.md"
第 6 步 — 报告
告知用户创建了哪些内容:
已创建资料集
<trove-id>,包含 N 个来源 — 提交为<TROVE_HASH:0:7>。
docs/troves/<trove-id>/manifest.yaml— 来源信息和元数据docs/troves/<trove-id>/sources/— N 个规范化来源文件docs/troves/<trove-id>/synthesis.md— 主题提炼:<SYNTHESIS_URL>在工件中通过以下内容引用:
trove: <trove-id>@<TROVE_HASH:0:7>
报告中始终包含综合文件 URL。一次运行创建多个资料集时,列出每个资料集的综合文件 URL。
扩展模式
向现有资料集添加新来源。
- 读取现有的
manifest.yaml - 收集并规范化新来源(与创建步骤 2 相同)
- 为新来源分配基于 slug 的来源 ID(遵循相同的 ID 生成规则)
- 将新条目追加到
manifest.yaml - 更新
refreshed日期 - 重新生成
synthesis.md,纳入所有来源(旧来源和新来源) - 追加一条
history记录,设置event: extended,并将commit: "--"作为占位符 - 提交并标记(与创建步骤 5 使用相同的双提交模式):
- 提交 A:`git commit -m "research(): extend with N new sources
Co-Authored-By: noreply@unknown"`
- 捕获
TROVE_HASH=$(git rev-parse HEAD) - 提交 B:将哈希回填到历史记录中,并更新引用此资料集的工件 frontmatter(如果工件存在)
- 推送(强制):
git push origin trunk - 生成
SYNTHESIS_URL(与创建步骤 5 使用相同的方法)
- 报告添加的内容,包括新的提交哈希和综合文件 URL
刷新模式
重新获取过期来源,并更新发生变化的内容。
- 读取
manifest.yaml - 对每个来源,检查
fetched日期加上freshness-ttl是否已过期 - 对过期来源:
- 重新获取原始内容
- 重新规范化为 Markdown
- 计算新的内容哈希
- 如果哈希发生变化:替换来源文件,并更新 manifest 条目
- 如果哈希未变化:仅更新
fetched日期
- 更新 manifest 中的
refreshed日期 - 如果任何内容发生变化,重新生成
synthesis.md - 追加一条
history记录,设置event: refreshed、sources-changed: M,并将commit: "--"作为占位符 - 提交并标记(与创建步骤 5 使用相同的双提交模式):
- 提交 A:`git commit -m "research(): refresh N sources (M changed)
Co-Authored-By: noreply@unknown"`
- 捕获
TROVE_HASH=$(git rev-parse HEAD) - 提交 B:将哈希回填到历史记录中,并更新引用此资料集的工件 frontmatter — 检查 manifest 中的
referenced-by,查找所有依赖项 - 推送(强制):
git push origin trunk - 生成
SYNTHESIS_URL(与创建步骤 5 使用相同的方法)
- 报告:“已刷新 N 个来源。M 个来源的内容发生变化,K 个来源未变化。新哈希:
<TROVE_HASH:0:7>。综合文件:<SYNTHESIS_URL>”
对于 freshness-ttl: never 的来源,刷新时跳过。
发现模式
帮助用户查找与其主题相关的现有资料集。
- 扫描
docs/troves/*/manifest.yaml,查找所有资料集 - 根据以下条件匹配用户的查询:
- 标签匹配 — 资料集标签包含查询关键词
- 标题匹配 — 资料集 ID 的 slug 包含查询关键词
- 对于每个匹配项,显示:资料集 ID、标签、来源数量、上次刷新日期、引用它的项目列表
- 如果没有匹配项,建议创建新的资料集
优雅降级
此技能会以通用方式引用各项能力。若某项能力不可用:
| 能力 | 回退方案 |
|---|---|
| 网络搜索 | 跳过基于搜索的来源。告知用户:“没有可用的网络搜索能力——请直接提供 URL,或添加搜索 MCP。” |
| 浏览器/页面抓取工具 | 尝试基本 URL 抓取。如果失败:“无法抓取此 URL——请粘贴内容或提供本地文件。” |
| 远程文档快照导出 | 如果导出失败且没有辅助工具:将来源标记为未经验证,不要发布到下游,并报告确切的 URL 和失败原因。 |
| 媒体转录 | “没有可用的转录能力——请提供预先制作的转录文件,或添加媒体转换工具。” |
| 文档转换 | “无法转换此文件类型——请提供 Markdown 版本,或添加文档转换工具。” |
| 付费墙代理 | 保留截断的内容。在清单中注明:“有付费墙;代理已用尽。”建议用户手动提供内容。 |
切勿因缺少某项能力而导致整个运行失败。尽可能收集信息,跳过无法完成的部分,并清楚地报告情况。
能力检测
收集来源前,检查可用的工具。查找符合以下模式的工具——具体工具名称因安装环境而异:
- 网络搜索:名称中包含 "search" 的工具(例如,
brave_web_search、bing-search-to-markdown) - 页面抓取:名称中包含 "fetch"、"webpage"、"browser" 的工具(例如,
fetch_content、webpage-to-markdown、browser_navigate) - 媒体转录:名称中包含 "audio"、"video"、"youtube" 的工具(例如,
audio-to-markdown、youtube-to-markdown) - 文档转换:名称中包含 "pdf"、"docx"、"pptx"、"xlsx" 的工具(例如,
pdf-to-markdown、docx-to-markdown) - CLI 工具捕获:内置 bash 能力(
man、命令执行)——在 POSIX 系统上始终可用
在开始收集时报告可用的能力,让用户了解哪些操作可行、哪些不可行。
从制品链接
制品通过 frontmatter 引用资料集:
trove: websocket-vs-sse@abc1234
格式为 <trove-id>@<commit-hash>。commit hash 会将资料集固定到特定版本——随着来源的添加或刷新,资料集会不断演进,而该 hash 可确保结果可复现。
Create 第 5 步、Extend 第 8 步和 Refresh 第 7 步中的双提交工作流会自动处理此事——提交 A 记录资料集内容,提交 B 则将 hash 写入历史记录条目和引用该资料集的制品 frontmatter。不要将此事推迟给操作人员处理。