mcpField
A
archive-crawler

archive-crawler

github.com/garrytan生产力31.3k203 次安装今天更新
代码仓库

关于 archive-crawler

archive-crawler 是用于探索个人档案并筛选有价值内容、导入 brain 的技能。它要求明确设置扫描路径白名单,用清单跟踪进度,并过滤噪声及敏感文档类型。

主要功能

  1. 01要求明确设置扫描路径白名单
  2. 02盘点并分类档案内容
  3. 03用清单跟踪审阅与导入状态
  4. 04展示前筛选内容
  5. 05将选定内容导入 brain 页面

适用场景

  1. 01审阅本地档案中的写作和通信
  2. 02整理 .mbox 等邮件导出文件
  3. 03探索获准访问的 Dropbox 或 Backblaze 档案

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/garrytan/gbrain --skill archive-crawler

安装后在 Claude Code 中输入/archive-crawler确认已加载。

附带文件routing-eval.jsonl

详细介绍

archive-crawler — 全能档案员

约定: 引用规则、用户反应的精确措辞要求以及反向链接执行,请参见 conventions/quality.md。

约定: 参见 _brain-filing-rules.md。本 Skill 是模式通用的:它从规则 JSON 中读取用户的归档规则,而不是硬编码任何特定的时代或档案结构。

安全闸门(必需,无例外)

除非在 gbrain.yml 中明确设置了 archive-crawler.scan_paths:,否则 archive-crawler 拒绝运行。这是一道有意设置的安全防线,用于防止 Agent 过度划定扫描范围,从而摄入敏感内容(税务 PDF、医疗记录、凭据)。

# gbrain.yml — the allow-list is mandatory
archive-crawler:
  scan_paths:
    - ~/Documents/writing/
    - ~/Dropbox/Archive/
    - /mnt/backup/old-letters/
  # Optional deny-list inside the allow-list:
  # deny_paths:
  #   - ~/Documents/finances/
  #   - ~/Documents/medical/

如果 scan_paths 为空或缺失,Skill 将以如下信息退出:

archive-crawler: refusing to run. No `archive-crawler.scan_paths:` allow-list
in gbrain.yml. Add explicit paths the agent is permitted to scan, then re-run.
This is a safety fence — the agent will not infer what's safe to read.

该约定由 src/core/storage-config.ts 强制执行(与 v0.22.11 存储分层中的 db_tracked / db_only 白名单模式保持一致)。

这是什么

在明确的白名单范围内探索任意个人内容目录树的通用引擎。适用于本地挂载、Dropbox API 目标、Backblaze B2、Gmail 导出文件(.mbox)以及类似档案。它会筛选出"金矿"(用户自己的写作、想法、人际关系),并以交互方式呈现供用户审阅。会跳过噪声内容(系统文件、配置、二进制文件)。

概念

来源

来源是任意待探索的文件目录树。来源具有以下属性:

  • type:local | dropbox | backblaze | gmail-takeout | mbox | pst
  • root:文件系统路径、Dropbox 路径、B2 前缀或 mbox 路径
  • manifest:一个用于追踪进度的 brain 页面,位于 projects/<archive-slug>/STATUS.md
清单

每次档案探索都会生成一个清单 brain 页面,用于追踪以下内容:

  1. 目录树清点:文件夹 / 文件 / 大小 / 类型
  2. 分拣状态:每一项的状态为 ⬜ 未查看 / 👀 已审阅 / ✅ 已摄入 / ⏭️ 跳过 / 🔥 高信号
  3. 用户反应:用户做出反应时的原话引文(遵循 conventions/quality.md 的精确措辞规则)
  4. 优先队列:接下来探索的内容,按优先级排序
  5. 会话日志:每次会话中所展示内容的带时间戳记录
金矿过滤器

在向用户展示任何内容之前,先应用金矿过滤器:

保留(展示)跳过(仅记录存在,不展示)
个人写作(日记、信件、感想、随笔)系统文件、配置、package.json、node_modules
对话(即时通讯记录、有实质内容的邮件往来)二进制文件(图片 / 视频)
想法、论点、框架收据、发票、税务文件
人际关系材料(写给重要之人或来自重要之人的信件)垃圾邮件、新闻简报、邮件列表群发
创作作品(诗歌、故事、有灵魂的代码)起源故事(后来变得重要之事物的最初版本)
情感内容(愤怒、爱、悲伤、发现)损坏 / 空文件

协议

第一阶段:清点

当指向一个新来源时:

  1. 确认已设置 scan_paths(安全闸门)。若未设置则退出。
  2. 绘制目录树:列出文件夹 + 文件 + 大小 + 日期范围。
  3. 对文件夹分类:按可能的内容类型分组(写作、邮件、代码、照片、文档、系统)。
  4. 创建清单:编写 projects/<archive-slug>/STATUS.md,包含完整清点内容。
  5. 提出优先队列:按可能的金矿密度对文件夹排序。
  6. 呈现给用户:展示目录图和建议的顺序,并允许用户进行调整。
Phase 2: Crawl

按优先级顺序处理各文件夹:

  1. 先读后展示:打开每个候选文件,应用 gold 过滤器,跳过噪声内容。
  2. 逐个展示:将 gold 条目逐一呈现,供用户审阅。
  3. 记录原始反应:按照 conventions/quality.md 的要求,在清单中用用户的原话记录其反应。
  4. 值得保留则入库:立即创建 brain 页面。
  5. 更新清单:每次交互后标记条目状态。
  6. 绝不重复展示:展示任何内容前,先检查清单。
Phase 3: Ingest

当某条目值得保留时,依据 _brain-filing-rules.md,按主要主题归档:

  • 用户自己的写作 / 想法 / 起源故事类内容 → originals/<slug>.md
  • 感悟 / 个人生活类内容 → personal/<slug>.md
  • 产品 / 商业想法 → ideas/<slug>.md
  • 关于某特定人物的信件或往来 → 在 people/<person>/timeline 添加反向链接,并将信件本身放在 personal/<slug>.md 或 originals/<slug>.md

该 Skill 与具体架构无关。 它不会内置任何特定的年代文件夹结构(例如 2003 年前的 originals/archive/、2019 年后的 originals/yc-era/ 等)。运行时会读取 _brain-filing-rules.json 中用户的归档规则;由 Agent 在每个页面上决定内容放置在这些许可目录中的哪个位置。

Brain 页面格式:

---
title: "[Title or first line]"
type: original
source_type: "[local|dropbox|backblaze|gmail-takeout|mbox|pst]"
source_path: "[path within the allow-listed scan_paths]"
date: "YYYY-MM-DD"  # date from the file metadata or content
people: ["person-1", "person-2"]
tags: ["tag-1", "tag-2"]
---

# [Title]

[Summary: what it is, when it's from, why it matters]

**User's reaction:** [exact quote, no paraphrasing]

## Context

[Cross-links to people, concepts, projects.]

---

[Raw source material below the line — full text]

文件类型处理器

纯文本 / HTML / Markdown

直接读取。展示时去除 HTML 标签。

.mbox(邮件归档)
import mailbox
mbox = mailbox.mbox('/path/to/file.mbox')
for msg in mbox:
    body = ''
    if msg.is_multipart():
        for part in msg.walk():
            if part.get_content_type() == 'text/plain':
                body = part.get_payload(decode=True).decode('utf-8', errors='replace')
                break
    else:
        body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
    # Apply gold filter
.doc / .docx
# .docx (modern)
python3 -c "
import zipfile, xml.etree.ElementTree as ET
with zipfile.ZipFile('/path/to/file.docx') as z:
    tree = ET.parse(z.open('word/document.xml'))
    print(''.join(t.text or '' for t in tree.iter('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}t')))
"

# .doc (legacy, requires antiword or catdoc)
antiword /path/to/file.doc 2>/dev/null || catdoc /path/to/file.doc 2>/dev/null
.pst(Outlook 归档)
# Validate first; many PSTs are null bytes
python3 -c "
with open('/path/to/file.pst', 'rb') as f:
    print('Valid PST' if f.read(4) == b'!BDN' else 'CORRUPT/NULL')
"
# If valid:
readpst -o /tmp/pst-output /path/to/file.pst
.zip / .tar / .tar.gz

解压到临时目录,然后递归遍历解压后的目录树。

图片

记录其存在性及元数据(文件名、大小、日期)。除非用户要求,否则不予展示。将扫描件 / 肖像类图片标记为可能涉及个人隐私。

清单模板

---
title: "[Archive Name] — Ingestion Status"
type: project
created: YYYY-MM-DD
updated: YYYY-MM-DD
source_type: "[local|dropbox|...]"
scan_paths: ["paths from gbrain.yml"]
---

# [Archive Name] — Ingestion Status

## Source
- **Type:** [local|dropbox|...]
- **Allow-listed paths:** [from gbrain.yml]
- **Total files:** [N]
- **Total size:** [X GB]
- **Date range:** [earliest] — [latest]

## Inventory

### [Folder 1]
| Item | Type | Size | Status | Reaction |
|------|------|------|--------|----------|
| file1.txt | text | 2KB | ✅ ingested | 🔥 "exact quote" |
| file2.doc | doc | 15KB | ⏭️ skip | — |
| file3.html | html | 4KB | ⬜ unseen | — |

### [Folder 2]
...

## Priority Queue
1. [Highest priority — why]
2. [Next — why]
...

## Session Log

### YYYY-MM-DD — [Session topic]
- Reviewed: [list]
- Reactions: [exact quotes]
- Ingested: [brain pages created]
- Next: [what's queued]

失败时的处理

对于任何 gbrain 错误 code、退出码、[AGENT] 代码块或通知代码块,请遵循 agent operator protocol。本 Skill 特有的情况如下:

  • 未设置 archive-crawler.scan_paths: 允许列表:这是本 Skill 自身的硬性拒绝。应询问用户哪些目录可以扫描;切勿擅自扩大列表以绕过此限制。
  • 归档挂载点、takeout 或存储桶不可读取(权限不足、令牌过期):报告失败的确切路径并停止该数据源;不要退而扫描父目录。
  • 归档某条发现时返回 write_pending(退出码 10)或 revision_conflict:对前者,轮询 gbrain write-request <request_id>;对后者,重新读取页面并合并。切勿以新的 slug 重新归档。

反模式

  • ❌ 未设置 archive-crawler.scan_paths: 就运行。这是硬性拒绝。这是安全约定,绝不可绕过。
  • ❌ 硬编码特定年代的归档路径(例如 originals/archive/、originals/yc-era/)。应在运行时读取归档规则。
  • ❌ 重复展示清单中已标记的条目。用户的时间是最稀缺的资源。
  • ❌ 改写用户的反应。只能使用用户的原话。
  • ❌ 将找到的内容包装成经验教训或收获。让故事自然呈现。
  • ❌ 当内容提及已有 brain 页面的人物或公司时,遗漏反向链接。依据 conventions/quality.md 中的铁律,必须添加。

相关 Skill

  • skills/voice-note-ingest/SKILL.md:用于音频采集的相同原话记录模式
  • skills/idea-ingest/SKILL.md:单个链接或文章的入库,采用相同的主要主题归档规则
  • skills/conventions/quality.md:引用、反向链接、语气

契约

本技能保证:

  • 路由与 frontmatter 中的标准触发词一致。
  • 输出写入 writes_to: 中列出的目录(如适用)。
  • 所引用的约定(quality.md、brain-first.md、_brain-filing-rules.md)均得到遵循。
  • 隐私契约得到保持:不包含真实姓名、不包含分叉特定的文件系统路径字面量,也不包含上游分叉引用。

完整的行为契约记录在上方正文各节中;本节存在的目的是供一致性测试使用。

输出格式

本技能的输出结构在正文各节中有内联说明(参见“Output”“Brain page format”或同类章节)。此处的字面章节标题存在的目的是供一致性测试(test/skills-conformance.test.ts)使用。

评论

支持 Markdown