关于 research-library
research-library 是一项用于维护跨项目参考资料目录的 skill,将仓库克隆、文档和网页快照集中存放在 $RESEARCH_HOME。它提供条目命名、来源记录、检查、更新和磁盘占用分析,避免项目重复保存资料。
主要功能
- 01集中管理跨项目参考资料
- 02添加仓库克隆并记录来源
- 03检查条目规范与来源信息
- 04更新、加深或缩浅克隆
- 05分析目录磁盘占用及不可搜索文件
适用场景
- 01为多个项目共用第三方代码参考
- 02在抓取网页或克隆仓库前检查现有资料
- 03刷新参考克隆并保留预期历史深度
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ theodoread/ agent-skills --skill research-library安装后在 Claude Code 中输入/research-library确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdevals/dependency-health.jsonreferences/dependency-health.mdrationale.mdscripts/library.pypackage_health.py详细介绍
研究资料库
$RESEARCH_HOME(默认为 ~/research)是一个跨项目共享的资料存储区,用于存放不应归属于任何单个 git 仓库的参考资料:供应商仓库克隆、PDF/epub、镜像的文档站点快照。它旨在避免两种情况:(1)Agent 仅仅因为未经审核的第三方内容恰好位于仓库工作树中,就在无意间读取这些内容;(2)每个项目都把相同的参考资料重新克隆到自己的 git 忽略目录中。
此技能假定资料库已存在。 它只是一个普通目录——没有任何内容会安装它,除以下约定外,也没有任何内容依赖其布局。如果 $RESEARCH_HOME 未设置且 ~/research 不存在,请说明这一点,并提出创建该目录,而不是悄悄改为从网络获取内容;在 shell 配置文件中设置 RESEARCH_HOME 是唯一的设置步骤。
此技能会读取、运行和写入什么
- 读取:
$RESEARCH_HOME及其中的条目;仓库自身的AGENTS.md(用于查找指引)。size --ungreppable会打开存储区中的每个文件,并读取其前 8 KB。 - 运行:仅在资料库克隆中运行
git clone/fetch/reset/sparse-checkout/tag -d/reflog expire/gc;运行gh api获取软件包健康状况(仓库统计数据、提交、问题、发布列表和最新发布),并在add克隆前获取仓库报告的大小。package_health.py还会在node位于PATH中时运行node --version,以比较最低版本要求;对于其apt来源,还会运行apt-cache policy和apt-cache show,它们只会读取此机器的 apt 缓存。 - 写入:仅在
$RESEARCH_HOME内写入——add会克隆并写入来源记录文件,provenance会写入该文件,update、deepen和reshallow会更改克隆及其depth:字段。name、check和size不会写入任何内容,而--dry-run会打印将要运行的命令。绝不会创建符号链接或将副本放入项目仓库,也绝不会触碰存储区以外的内容。 - 从环境读取:
RESEARCH_HOME(用于指定存储区),以及RESEARCH_TEXT_ONLY(用于指定add默认是否仅克隆文本内容)。 - 网络:你提供的克隆 URL;PyPI、npm registry、crates.io、Debian 的 madison API(
api.ftp-master.debian.org)、Launchpad(api.launchpad.net)和 GitHub(供package_health.py使用);用于预克隆大小查询的 GitHub;以及通过你自己的gh登录访问的 GitHub。仅使用公开的只读端点;不会上传任何内容。
从网络获取任何内容之前
在尝试使用 WebFetch/WebSearch 或克隆新副本之前,先检查 $RESEARCH_HOME/repos/、$RESEARCH_HOME/docs/ 和 $RESEARCH_HOME/pages/,确认其中是否已有相关主题的资料。项目自身的 AGENTS.md 可能已经指出与该项目相关的具体条目。
布局
$RESEARCH_HOME/
repos/<host>--<owner>--<repo>/ # shallow git clones
docs/<file>.pdf|.epub # downloaded reference docs
pages/<slug>/ # mirrored/llms.txt-derived doc site snapshots
README.md # full conventions + rationale
命名、添加和检查条目
每个主机上的每个仓库都遵循 <host>--<owner>--<repo> 这一约定,没有例外,也没有 GitHub 特例;每个条目都带有来源文件。不要手动推导名称或来源信息——scripts/library.py 负责推导,这正是它存在的原因:这两类错误都不会显现出来。根据提供给你的 URL 而不是克隆自身的 origin 推导名称,看起来完全正常(自托管实例与热门主机相似,重命名后的仓库会静默重定向);而对存储库中没有版本控制的内容,也没有其他东西能发现问题。
python3 $S/scripts/library.py name <url> # the entry name, from any spelling of a remote
python3 $S/scripts/library.py add <url> # clone, canonical name, SOURCE.md written
python3 $S/scripts/library.py add <url> --dry-run # the clone and the file it would write
python3 $S/scripts/library.py check # every entry against these conventions
S=~/.agents/skills/research-library。add 会在克隆后根据克隆自身的远程地址重新推导名称,并在名称不一致时重命名条目;它会将实际获取的分支和短 sha 记录为 ref,且除非另有说明,否则只克隆文本内容——关于这会排除哪些内容,以及为何不会影响搜索,见下文。对于非 git 克隆的条目——例如下载的 PDF、镜像的文档站点——请根据材料的要求获取内容,然后运行 library.py provenance <path> --url <url> --kind <kind> --ref <ref> 写入元数据部分;这部分是可确定的,也是容易被跳过的部分。所有命令都支持 --json;name 和 check 只读,add 和 provenance 只会在 $RESEARCH_HOME 内写入内容。
来源文件的格式如下,因为 check 会强制检查该格式,读者可能需要手动修复文件:url、kind(repo-clone、llms-txt-mirror 或 site-mirror)、ref(克隆时为分支/标签/提交,镜像时为获取日期)、fetched、有意加深历史记录时使用的 depth(见下文)、缩减检出范围时使用的 text-only(见下文),以及仅在情况不明显时填写的 note——例如,文档发布自与克隆来源不同的分支或仓库。
如果主机报告某个仓库很大,add 会在克隆前询问;它会以状态码 3 退出并显示报告的数值,而不会发起提示——因为 Agent 的 Bash 调用中的交互式提示会卡住,且无处输入。使用 --yes 继续,使用 --min <MB> 调整阈值(默认值为 250 MB),使用 --depth N 或 --full 克隆多个提交并记录相关信息。检查所用的是 GitHub 的 API,因此它无法请求信息的主机,不会因此被拒绝克隆。
**报告的大小只是触发警告的条件,而不是预测值;警告也有意不提供任何估算值。**根据 2026-09-07 对 5 个真实条目进行的测量(深度为 1):磁盘占用量从报告大小的 0.23 倍(cpython,报告为 851 MB,磁盘占用 192 MB)到 1.32 倍(Roo-Code,报告为 359 MB,磁盘占用 473 MB)不等。差异达到 5.7 倍,而且这甚至不是上限——如果警告写出预测数值,那么在让人放心的方向上,误差会达到 4 倍。
克隆只保留文本,因为大家搜索的也只有文本
add 默认只克隆文本文件:图片、视频、音频、字体、压缩包、编译后的对象和模型权重会留在服务端,所有文本搜索能够读取的内容都会被检出。--all-files 可让单个条目不受此规则限制;RESEARCH_TEXT_ONLY=0 可让整台机器不受此规则限制。
python3 $S/scripts/library.py add <url> # text-only, the default
python3 $S/scripts/library.py add <url> --all-files # every file, images and binaries included
python3 $S/scripts/library.py add <url> --dry-run # the two commands and the file it would write
**排除内容不能让搜索少命中一个结果,这正是它成为默认设置的唯一原因。**它跳过的每种格式,ripgrep 和 grep 本就拒绝搜索——二者都会将首个数据块中含有 NUL 字节的文件视为二进制文件并跳过——因此这些字节对搜索毫无帮助。这也说明它与排除某个 目录 并非同一决定;排除目录会让 grep 在不提示的情况下漏掉内容,而我们正是出于这个原因,始终没有采用这种做法。
2026-09-08 在 intellectronica/ruler 上端到端验证,该仓库有 97% 的内容是二进制文件;同一提交分别以两种方式克隆:两个工作树中都有 257 个可搜索文件,且文件集合完全相同,grep -rIl ruler 在两者中都返回 190 个文件,而纯文本克隆中少了一个文件——一个 69 MB 的 .gif。磁盘占用从 141 MB 降至3.0 MB,.git 从 70 MB 降至 644 KB。
最后这个数字才是值得关注的,因为它来自两项单独使用时都毫无作用的设置的组合。--filter=blob:none 用在 --depth 1 克隆中不会延迟任何内容,因为检出时仍会物化 HEAD 中的每个 blob;单独使用稀疏集合只能缩小工作树,而 .git 的大小完全不变。两者结合后,稀疏集合永远不需要的 blob 就永远不会被获取。克隆时只使用其中一项,节省的空间都远不如表面看起来那么多。
文档会保留,PDF 正是原因。PDF 对 grep 来说是二进制文件,对 Agent 来说却可读,因此划分标准是文档与演示素材,而非能否被 grep 搜索。目前仓库克隆中共有 9 个 PDF,占 6.6 MB,即排除内容总大小的 0.5%,其中包括一个 4.6 MB 的 flameshot-documentation.pdf。只牺牲半个百分点的节省空间,就能排除唯一可能导致读者想要的内容被遗漏的情况。电子书和办公文档格式也基于同样的判断予以保留。.svg 则完全无需例外:它是文本,因此 NUL 规则会保留它。
如果某个条目的检出范围比其 HEAD 更窄,就必须明确说明,否则后续 grep 搜不到内容时,结果看起来会像是答案,而不是在提醒人们确认检出了哪些内容。add 会记录 text-only: yes,而 check 会报告两个方向的偏差——条目声称启用了此设置,但实际检出了全部内容;以及存在稀疏检出,却没有任何记录。这也能发现有人手动应用了基于路径的 sparse-checkout。
update 无需特殊处理:浅层获取和硬重置会保留 core.sparseCheckout 及其模式列表,已在实际刷新操作中确认。
库的成本
python3 $S/scripts/library.py size # entries at or above 250 MB, biggest first
python3 $S/scripts/library.py size --min 0 # everything
python3 $S/scripts/library.py size --ungreppable # and how much of it no text search can read
--ungreppable 让重新克隆已有条目时仅克隆文本内容成为一个有具体数字支撑的选择,而非原则。它按内容分类,而不是按排除列表分类,因此报告可能与这些模式相矛盾——这也正是无扩展名的残留文件仍然可见的原因:整个库中有 620 个文件、共 16 MB,占不可搜索内容总量的 1.25%,且无法通过任何扩展名模式筛选出来。这个规模足够小,可以直接作为答案,而不必再加一道筛选。它会读取存储中的每个文件(在 3 GB 的库中,184,279 个文件的预热读取耗时为 2.9 秒),因此它是一个标志选项。
截至 2026-09-08,对 86 个条目进行测量后发现:工作树共 3,274 MB,其中 1,286 MB、占 39% 的内容不可搜索。
这些内容主要集中在少数条目中——block/goose 占 310 MB(总计 343 MB),RooCodeInc/Roo-Code 占 276 MB(总计 293 MB)——而库中最大的条目则是一个反例,说明路径排除应当作为单独的问题处理:
nodejs/node 有 669 MB,其中仅 15 MB 不可搜索;其 669 MB 的工作树包含的是供应商提供的 V8 和 OpenSSL源代码,每个字节都可搜索。
对于每个条目,它会将 .git 与工作树分开统计,因为两者的解决方法不同,而比例能说明该采用哪种方法:在单个提交中 .git 很大,说明存在大型 blob,唯一的办法是不保留该克隆;在单个提交中工作树很大,说明存在供应商提供的目录。我们在一个实际的 4.8 GB 库中对最糟糕的条目进行了测量:单个提交就占 940 MB,其中一个供应商提供的 deps/ 目录就占了 675 MB,而参考克隆中存在的 src、lib 和 doc 合计只有 34 MB。这里的问题不在于克隆深度,无论怎么缩减克隆深度都无法改变这一点。
更新、加深和回收磁盘空间
python3 $S/scripts/library.py update # every clone, each at its intended depth
python3 $S/scripts/library.py update <entry> # just this one
python3 $S/scripts/library.py deepen <entry> --depth 500 # more history, recorded as deliberate
python3 $S/scripts/library.py reshallow <entry> # back to a depth-1 footprint, disk included
update 会将每个克隆更新到其远程仓库的最新状态——执行浅层获取和硬重置,因为这些克隆只是可丢弃的参考副本,并非需要保留本地提交的工作副本。它和 library.py check 回答的是不同问题,彼此不能替代:前者推进所有克隆,后者则判断推进克隆是否可能产生任何效果。
对于没有记录且深度超过一个提交的克隆,会跳过而不是截断。 无法区分有意加深和意外加深,而截断是无法通过读取来撤销的操作。2026-09-07 在一个包含 71 个条目的知识库中确认:恰好有一个条目是深度克隆,它是为了查看某个依赖项的约束历史而有意加深的;如果用 fetch --depth 1 循环刷新所有内容,就会悄无声息地破坏它。请使用 deepen,不要手动输入 git fetch,这样意图会记录在条目的 depth: 字段中,update 也就能识别。
reshallow 的存在,是因为手动重新浅化并不能回收磁盘空间。 2026-09-07 在 encode/httpx 上测量,.git 的大小(KB):全新执行 --depth 1 的克隆为 2,492;加深 400 个提交后,再用 fetch --depth 1 和 reset --hard 重新浅化,报告显示又只剩一个提交,但磁盘占用完全没有变化;再加上 reflog expire、gc --prune=now、repack -a -d、prune 和 gc --aggressive,占用达到 4,852 后便不再下降。原因是标签——加深操作会一并获取标签,每个标签都会固定历史深处的一个提交,因此其下的所有对象仍可访问。删除这些标签后,同一个克隆的占用降至 2,472,甚至低于最初的大小。广为流传的操作步骤漏掉了这一步,因此照原样执行会让克隆永久大出 95%,却仍报告成功。整个过程有五条命令,必须按顺序执行,其中一条不见于任何参考资料:这恰好是适合写进代码的操作。
如果 HEAD 处于分离状态,它会拒绝执行,因为标签可能正是此克隆存在的用途;--force 可强制覆盖。
刷新后看起来异常陈旧的条目,通常是被固定到了某个版本,check 会指出它:使用显式 --branch <tag> 创建的克隆会让 HEAD 处于分离状态,并永远跟踪那一个引用,因此 git fetch origin 会反复获取同一个内容,刷新也会报告内容“已是最新”,即使它已经陈旧多年。2026-09-02,在一个包含 52 个条目的知识库上首次运行 check 时确认:有一个这样的克隆;而另外 51 个条目在克隆时完全遵循了本技能的说明,若采用更粗糙的规则,也会被误判为固定版本。check --remote 会额外检查一个需要联网才能回答的问题——正在跟踪的分支是否仍是远程仓库的默认分支;默认情况下此选项关闭,因此其余检查仍然即时完成。
检索真实源码,不要轻信文档或 README 的描述
克隆仓库后,优先检索实际源码来确认事实,而不是相信 README 或文档网站的描述——相对于已安装的版本,两者都可能已经过时或存在错误。这曾经发现过真实的 bug:某个文档页面描述了一种 GNOME 键绑定架构,但已安装的 GNOME 版本中实际上并不存在(只有直接阅读 gnome-shell 源码才发现这一点);另一个文档站点构建工具的实际挂载行为,只有在它实际的压缩 JS 包中才与文档相符,而不是渲染后的文档页面。
这两种情况都是文档过时。还有一种更糟的情况:README 宣传的功能根本就没有实现。2026-08-27,在为 ingesta 调研用药追踪器时确认了这一点——某个 Home Assistant 集成的 README 将 NIH RxNorm 成分查询列为功能之一,但除了 README 外,仓库中任何地方都没有出现 rxnorm 或 ingredient。功能列表对比会认为它具备这一功能;检索一次就能定论。当问题是“这个项目能否完成 X”时,最简单且有决定性的检查,是看代码里是否出现 X,而不是文档是否声称支持它。
这也是为什么对于开源候选项目,在条件允许时源码胜过动手试用:试用只能覆盖你碰巧走过的路径,源码则展示所有路径,并能回答界面无法揭示的问题——剂量是数字还是自由文本、某项权限是否已从清单中移除。
评估候选依赖项
选择库或工具,与上面的规则是同一件事,只是更早一步:问题在于源码说明了什么,而不是摘要声称了什么。根据软件包自身的注册表元数据和文件列表来评估,而非依据搜索摘要——找到软件包后,就该查看这些内容。
**在进行正面对比之前,先单独根据绝对标准评估每个候选项。**超过某个门槛后,受欢迎程度只是微弱信号,绝不能用来打破平局——达到门槛但不那么受欢迎的项目,胜过更受欢迎却未达到门槛的项目。2026-08-30,在两个 Telegram 库之间做选择时提出了这一点;这也是脚本将星标数打印在 not scored 下方的原因。
python3 $S/scripts/package_health.py pypi <name>
python3 $S/scripts/package_health.py pypi anyio --clone $RESEARCH_HOME/repos/github.com--agronholm--anyio
python3 $S/scripts/package_health.py pypi shellcheck-py --upstream koalaman/shellcheck
python3 $S/scripts/package_health.py npm @biomejs/biome
python3 $S/scripts/package_health.py crates ripgrep
python3 $S/scripts/package_health.py apt ripgrep
python3 $S/scripts/package_health.py github BurntSushi/ripgrep
仅使用标准库,S=~/.agents/skills/research-library。必须将来源作为子命令——pypi、npm、crates、apt 或 github;没有默认值,因此每次都要明确指定;已废弃的 package_health.py <name> <owner/repo> 格式会以退出码 2 结束,并指出替代命令。若同一工具通过多个渠道提供——PyPI 或 npm 封装包、crate、apt 包、发行版二进制文件——就分别检索每个渠道的来源,并比较它们包含的内容和成本。软件包自己的 GitHub 仓库会从其元数据中读取,报告中的 repo from 行会说明来源;若查找结果错误或缺失,请传入 --repo <owner/repo>。注册表通过 HTTPS 查询,GitHub 则通过 gh api 查询,以使用你自己的令牌和速率限制。每种来源都使用 --json 获取完整答案。
每种来源在适用时都会报告以下维度,以及各维度的用途:
- 维护情况——stable 发布线上的版本及其中位发布时间间隔、最近一次推送距最近一次发布的时间(用于区分“积极开发但发布缓慢”和“已停滞”)、过去一年的人类贡献者数量和巴士系数、问题关闭所需时间、归档标记、许可证,以及已撤回或已弃用的版本。
- 类型检查,仅适用于动态类型生态系统——PyPI 的
py.typed(使用--clone时),以及 npm 自带类型、单独的@types/…和两者皆无这三种情况。对于 Python,还包括项目自身的类型检查器配置及其严格程度,这能预测有哪些类型问题会漏过。然后进行测量:使用你的检查器,以你的模式检查一小段真实用例。只有这样才能知道你的门禁会给出什么结果。 - 经过实战检验——使用
pypi --clone:测试与手写源代码的比例(--generated <glob>用于标记机械生成的部分)、覆盖率是在 CI 中强制执行还是仅报告,以及 CI 工作流实际覆盖了哪些内容。 - 适配性——运行时依赖项的数量和名称(绝不使用原始
requires_dist,因为其中大多是额外依赖项)、版本上限及其约束的是所分发的制品还是仅开发锁定文件、许可证兼容性,以及能否离线运行。 - 交付内容——安装最新稳定版本实际下载的内容及其大小,即采用成本。不同来源对此的含义不同;见下文。
- 最低要求——元数据中声明的所有最低版本,绝不推断;在成本较低时,逐项与此机器比较:
requires_python对比此处的python3,manylinux标签或libc6依赖项声明的 glibc 最低版本对比此机器的 glibc,engines.node对比此处的node,crate 的 MSRV,以及制品名称所声明的 libc 家族。NOT MET HERE表示检查结果;not compared说明未比较的原因。 - 上游项目,在
pypi和npm上使用--upstream <owner/repo>——对于封装器,指出它重新打包的项目;该项目并非封装器自己的仓库。它会报告上游最新的稳定版 GitHub 发布、为与其匹配而写出的封装器版本及滞后天数,以及该版本面向 Linux x86_64 的制品和旁边的任何校验和或签名文件。NO MATCHING WRAPPER RELEASE表示版本写法不同或封装器版本落后:比较打印出的两个版本。GitHub digest是 GitHub 对上传文件计算的哈希,并非发布者提供的校验和。
各来源提供的内容:
pypi <name>— wheel 和 sdist、它们的标签、大小及上传日期。在采用封装二进制文件的包装器之前,先查看linux x86_64这一行:如果存在 wheel,说明二进制文件已包含其中;如果显示SDIST ONLY或NO WHEEL for this machine,则每次安装时 pip 都会构建它,或者由构建钩子下载它。--clone <path>可补充 API 无法回答的信息——py.typed、测试与源代码的比例、CI 清单,以及实际存在的许可证文件。npm <name>— 以dist-tags.latest为准,而不是最新发布版本。install scripts(RUNS AT INSTALL表示安装时会在你的机器上执行代码)、provenance,以及实际携带二进制文件的平台包——默认会解析平台包,因为像@biomejs/biome这样的包装器大小为 779 kB,而其 Linux 包为 64.7 MB。install size是两者的总大小。crates <name>— crates.io 自身的稳定版本、crate 的大小和bin_names,以及 MSRV(为 null 时显示undeclared)。crate 是由cargo install编译的源代码;如果其中包含二进制文件,prebuilt binary部分会列出最新 GitHub 版本的 Linux 资源。apt <name>— 这是另一个问题,报告也会明确说明:apt 包由发行版维护。报告会给出这台机器的候选版本及其维护者(main由 Canonical 提供支持;universe只有 Ubuntu Pro 用户才能获得 Canonical 安全修复;第三方仓库则由其发布者维护)、大小、与上游稳定 GitHub 版本之间的滞后(从包的Homepage读取上游信息,或使用--upstream),以及跨发行版视图,显示哪些 Ubuntu 和 Debian 版本包含哪个版本——可据此选择 apt 还是发布二进制文件,也可了解下一个 LTS 会带来什么。github <owner/repo>— 适用于完全没有注册表的工具,例如只通过发布资源提供的 Go 或 Rust 二进制文件:维护情况、从发布列表得出的稳定版本发布节奏,以及最新稳定版本的 Linux x86_64 资源。
报告中的四行看似给出了答案,实则暗藏陷阱,务必按原样解读:open issues+PRs 是 GitHub 的字段,统计了问题和 PR;PRE-RELEASE ONLY 或 pre-releases 表示开发版本仍在更新,但稳定版本可能没有更新;shallow clone 表示任何涉及历史记录的问题都需要先运行 git fetch --deepen;npm 的 tarball 大小仅指包装器,真正值得关注的是 install size。在 httpx 上于 2026-09-02 确认:若只看表面,PyPI 显示过去一年发布了 4 个版本,最新版本就在昨天。而稳定版本一栏显示的是过去一年零次发布,距上次发布已 634 天——这与“它是否仍在维护,适合我使用”的答案截然相反。
数字无法揭示的一切——主导 bus factor 的机器人、API 报告为 GPL 的双重许可项目、为何版本上限在其历史滞后情况表明之前并不构成成本、如何证明可离线测试,以及各注册表自身的陷阱——都记录在
references/dependency-health.md 中。请在撰写建议之前阅读,而不是在运行脚本之前阅读。
不要创建指向项目仓库的符号链接
绝不要将 $RESEARCH_HOME 或其中的任何条目符号链接到项目的工作树中。这样会让这些内容重新进入任何限定于该仓库的环境的默认读取路径——这正是它们存放在所有仓库之外的原因。只有任务确实需要时,才直接通过 $RESEARCH_HOME 路径访问。
按项目设置指引
此技能无法知道哪些条目与特定项目相关。每个项目自己的 AGENTS.md 都应列出与该项目相关的具体路径(“关于 GNOME Shell 扩展的行为,在线查阅任何内容之前,请先检查 $RESEARCH_HOME/repos/gitlab.gnome.org--GNOME--gnome-shell”),并将其作为固定规则,而非建议。这也能让只能读取 AGENTS.md、但没有技能发现机制的 Agent 访问该资料库。
完整设计原理
references/rationale.md — 说明了为什么选择这种做法,而不是为每个仓库创建一个 reference/ 目录;为什么命名方式不对热门托管平台做特殊处理;固定标签刷新陷阱;对文档站点镜像(llms.txt 及其备用方案)的调研;以及为什么研究了 RAG/embeddings 后,决定不采用。