[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6N022KB4YJSZDS9D00A01NB":3,"skill-same:01M6N022KB4YJSZDS9D00A01NB":90},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":62,"commitSha":63,"skillDirectory":64,"forksCount":65,"repositoryHtmlUrl":66,"repositoryHomepageUrl":6,"ownerHtmlUrl":67,"ownerBlogUrl":68,"ownerDescription":69,"updatedAt":70,"install":71,"resources":83},"01M6N022KB4YJSZDS9D00A01NB","archive-crawler",null,"Universal archivist for personal file archives (Dropbox\u002FB2\u002FGmail-takeout\u002Flocal-mount\u002Fhard-drive-dump). Filters for high-value content (the user's own writing, ideas, relationships) and surfaces it interactively. REFUSES TO RUN without an explicit gbrain.yml `archive-crawler.scan_paths:` allow-list.","garrytan\u002Fgbrain","garrytan","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F19957?v=4","01M6DJCCDDG53MTYYRTXAXFG17","Garry Tan","01M6MZQZCEN3RRMD33E1X9ZZR9",32517,"MIT","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Fplugin\u002Fskills\u002Farchive-crawler\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"archive crawler","archive","crawler","local","when","value","Archivist","my","paths","mine","dump","ideas","writing","crawl","surfaces","files","filters","list","run","personal","gmail","2026-11-10 10:12:30",false,"---\nname: archive-crawler\nversion: 0.1.0\ndescription: Universal archivist for personal file archives (Dropbox\u002FB2\u002FGmail-takeout\u002Flocal-mount\u002Fhard-drive-dump). Filters for high-value content (the user's own writing, ideas, relationships) and surfaces it interactively. REFUSES TO RUN without an explicit gbrain.yml `archive-crawler.scan_paths:` allow-list.\ntriggers:\n  - \"crawl my archive\"\n  - \"find gold in my archive\"\n  - \"archive crawler\"\n  - \"scan my dropbox for\"\n  - \"mine my old files for\"\nmutating: true\nwrites_pages: true\nwrites_to:\n  - originals\u002F\n  - personal\u002F\n  - ideas\u002F\nwhen_to_use: \"Use when the user asks: \\\"crawl my archive\\\", \\\"find gold in my archive\\\", \\\"archive crawler\\\", \\\"scan my dropbox for\\\", \\\"mine my old files for\\\".\"\n---\n\n# archive-crawler — The Universal Archivist\n\n> **Convention:** see [conventions\u002Fquality.md](..\u002Fconventions\u002Fquality.md) for\n> citation rules, exact-phrasing requirements when capturing the user's\n> reactions, and back-link enforcement.\n>\n> **Convention:** see [_brain-filing-rules.md](..\u002F_brain-filing-rules.md) —\n> this skill is **schema-generic**: it reads the user's filing rules from\n> the rules JSON instead of hardcoding any specific era \u002F archive layout.\n\n## Safety gate (REQUIRED, no exceptions)\n\narchive-crawler refuses to run unless `archive-crawler.scan_paths:` is\nexplicitly set in `gbrain.yml`. This is a deliberate safety fence against\nthe agent over-scoping a scan and ingesting sensitive content (tax PDFs,\nmedical records, credentials).\n\n```yaml\n# gbrain.yml — the allow-list is mandatory\narchive-crawler:\n  scan_paths:\n    - ~\u002FDocuments\u002Fwriting\u002F\n    - ~\u002FDropbox\u002FArchive\u002F\n    - \u002Fmnt\u002Fbackup\u002Fold-letters\u002F\n  # Optional deny-list inside the allow-list:\n  # deny_paths:\n  #   - ~\u002FDocuments\u002Ffinances\u002F\n  #   - ~\u002FDocuments\u002Fmedical\u002F\n```\n\nIf `scan_paths` is empty or missing, the skill exits with:\n\n```\narchive-crawler: refusing to run. No `archive-crawler.scan_paths:` allow-list\nin gbrain.yml. Add explicit paths the agent is permitted to scan, then re-run.\nThis is a safety fence — the agent will not infer what's safe to read.\n```\n\nThis contract is enforced by `src\u002Fcore\u002Fstorage-config.ts` (mirrors the\n`db_tracked` \u002F `db_only` allow-list pattern from v0.22.11 storage tiering).\n\n## What this is\n\nGeneric engine for exploring any tree of personal content within an\nexplicit allow-list. Works on local mounts, Dropbox API targets,\nBackblaze B2, Gmail takeouts (`.mbox`), and similar archives. Filters\nfor \"gold\" (the user's own writing, ideas, relationships) and surfaces\nit interactively for review. Skips noise (system files, configs, binary\nblobs).\n\n## Concepts\n\n### Source\n\nA source is any tree of files to explore. Sources have:\n\n- **type**: `local` | `dropbox` | `backblaze` | `gmail-takeout` | `mbox` | `pst`\n- **root**: filesystem path, Dropbox path, B2 prefix, mbox path\n- **manifest**: a brain page tracking progress at\n  `projects\u002F\u003Carchive-slug>\u002FSTATUS.md`\n\n### Manifest\n\nEvery archive exploration gets a manifest brain page that tracks:\n\n1. **Tree inventory** — folders \u002F files \u002F sizes \u002F types\n2. **Triage status** — each item: `⬜ unseen` \u002F `? reviewed` \u002F\n   `✅ ingested` \u002F `⏭️ skip` \u002F `? high-signal`\n3. **User reactions** — exact quotes when they react (per\n   conventions\u002Fquality.md exact-phrasing rule)\n4. **Priority queue** — what to explore next, ranked\n5. **Session log** — timestamped record of what was shown per session\n\n### Gold filter\n\nBefore showing anything to the user, apply the gold filter:\n\n| Keep (show) | Skip (note existence, don't show) |\n|-------------|-----------------------------------|\n| Personal writing (journals, letters, reflections, essays) | System files, configs, package.json, node_modules |\n| Conversations (IM logs, email threads with substance) | Binary blobs (images \u002F video) |\n| Ideas, theses, frameworks | Receipts, invoices, tax docs |\n| Relationship material (letters to \u002F from people who matter) | Spam, newsletters, mailing-list bulk |\n| Creative work (poetry, stories, code with soul) | Corrupted \u002F null files |\n| Origin stories (first versions of things that became important) | |\n| Emotional content (anger, love, grief, discovery) | |\n\n## Protocol\n\n### Phase 1: Inventory\n\nWhen pointed at a new source:\n\n1. **Confirm scan_paths is set** (safety gate). Exit if not.\n2. **Map the tree** — list folders + files + sizes + date ranges.\n3. **Classify folders** — group by likely content type (writing, email,\n   code, photos, docs, system).\n4. **Create manifest** — write `projects\u002F\u003Carchive-slug>\u002FSTATUS.md` with\n   the full inventory.\n5. **Propose priority queue** — rank folders by likely gold density.\n6. **Present to user** — show the map and proposed order. Let them\n   override.\n\n### Phase 2: Crawl\n\nWork through folders in priority order:\n\n1. **Read before showing** — open each candidate file, apply the gold\n   filter, skip noise.\n2. **Show one at a time** — present gold items individually for review.\n3. **Capture exact reaction** — track the user's response in the\n   manifest using their exact words (per conventions\u002Fquality.md).\n4. **Ingest if worth keeping** — create a brain page immediately.\n5. **Update manifest** — mark item status after each interaction.\n6. **Never re-show** — check the manifest before presenting anything.\n\n### Phase 3: Ingest\n\nWhen an item is worth keeping, file it by **primary subject** per\n`_brain-filing-rules.md`:\n\n- User's own writing \u002F ideas \u002F origin-story content → `originals\u002F\u003Cslug>.md`\n- Reflections \u002F personal-life content → `personal\u002F\u003Cslug>.md`\n- Product \u002F business ideas → `ideas\u002F\u003Cslug>.md`\n- Letters or threads about a specific person → `people\u002F\u003Cperson>\u002Ftimeline`\n  back-link plus the letter at `personal\u002F\u003Cslug>.md` or `originals\u002F\u003Cslug>.md`\n\n**The skill is schema-generic.** It does NOT bake in any specific\nera-folder structure (e.g., `originals\u002Farchive\u002F` for pre-2003,\n`originals\u002Fyc-era\u002F` for post-2019, etc.). The user's filing rules from\n`_brain-filing-rules.json` are read at runtime; the agent decides per-page\nwhere content lands within those sanctioned directories.\n\nBrain page format:\n\n```markdown\n---\ntitle: \"[Title or first line]\"\ntype: original\nsource_type: \"[local|dropbox|backblaze|gmail-takeout|mbox|pst]\"\nsource_path: \"[path within the allow-listed scan_paths]\"\ndate: \"YYYY-MM-DD\"  # date from the file metadata or content\npeople: [\"person-1\", \"person-2\"]\ntags: [\"tag-1\", \"tag-2\"]\n---\n\n# [Title]\n\n[Summary: what it is, when it's from, why it matters]\n\n**User's reaction:** [exact quote, no paraphrasing]\n\n## Context\n\n[Cross-links to people, concepts, projects.]\n\n---\n\n[Raw source material below the line — full text]\n```\n\n## File-type handlers\n\n### Plain text \u002F HTML \u002F Markdown\nRead directly. Strip HTML tags for display.\n\n### `.mbox` (email archives)\n\n```python\nimport mailbox\nmbox = mailbox.mbox('\u002Fpath\u002Fto\u002Ffile.mbox')\nfor msg in mbox:\n    body = ''\n    if msg.is_multipart():\n        for part in msg.walk():\n            if part.get_content_type() == 'text\u002Fplain':\n                body = part.get_payload(decode=True).decode('utf-8', errors='replace')\n                break\n    else:\n        body = msg.get_payload(decode=True).decode('utf-8', errors='replace')\n    # Apply gold filter\n```\n\n### `.doc` \u002F `.docx`\n\n```bash\n# .docx (modern)\npython3 -c \"\nimport zipfile, xml.etree.ElementTree as ET\nwith zipfile.ZipFile('\u002Fpath\u002Fto\u002Ffile.docx') as z:\n    tree = ET.parse(z.open('word\u002Fdocument.xml'))\n    print(''.join(t.text or '' for t in tree.iter('{http:\u002F\u002Fschemas.openxmlformats.org\u002Fwordprocessingml\u002F2006\u002Fmain}t')))\n\"\n\n# .doc (legacy, requires antiword or catdoc)\nantiword \u002Fpath\u002Fto\u002Ffile.doc 2>\u002Fdev\u002Fnull || catdoc \u002Fpath\u002Fto\u002Ffile.doc 2>\u002Fdev\u002Fnull\n```\n\n### `.pst` (Outlook archives)\n\n```bash\n# Validate first; many PSTs are null bytes\npython3 -c \"\nwith open('\u002Fpath\u002Fto\u002Ffile.pst', 'rb') as f:\n    print('Valid PST' if f.read(4) == b'!BDN' else 'CORRUPT\u002FNULL')\n\"\n# If valid:\nreadpst -o \u002Ftmp\u002Fpst-output \u002Fpath\u002Fto\u002Ffile.pst\n```\n\n### `.zip` \u002F `.tar` \u002F `.tar.gz`\n\nExtract to a temp dir, then recurse through the extracted tree.\n\n### Images\n\nNote existence + metadata (filename, size, date). Don't show unless the\nuser asks. Flag scans \u002F portraits as potentially personal.\n\n## Manifest template\n\n```markdown\n---\ntitle: \"[Archive Name] — Ingestion Status\"\ntype: project\ncreated: YYYY-MM-DD\nupdated: YYYY-MM-DD\nsource_type: \"[local|dropbox|...]\"\nscan_paths: [\"paths from gbrain.yml\"]\n---\n\n# [Archive Name] — Ingestion Status\n\n## Source\n- **Type:** [local|dropbox|...]\n- **Allow-listed paths:** [from gbrain.yml]\n- **Total files:** [N]\n- **Total size:** [X GB]\n- **Date range:** [earliest] — [latest]\n\n## Inventory\n\n### [Folder 1]\n| Item | Type | Size | Status | Reaction |\n|------|------|------|--------|----------|\n| file1.txt | text | 2KB | ✅ ingested | ? \"exact quote\" |\n| file2.doc | doc | 15KB | ⏭️ skip | — |\n| file3.html | html | 4KB | ⬜ unseen | — |\n\n### [Folder 2]\n...\n\n## Priority Queue\n1. [Highest priority — why]\n2. [Next — why]\n...\n\n## Session Log\n\n### YYYY-MM-DD — [Session topic]\n- Reviewed: [list]\n- Reactions: [exact quotes]\n- Ingested: [brain pages created]\n- Next: [what's queued]\n```\n\n## When it fails\n\nFollow the [agent operator protocol](..\u002Fconventions\u002Fagent-operator-protocol.md) for any gbrain error `code`, exit code, `[AGENT]` block or notice block. Specific to this skill:\n\n- No `archive-crawler.scan_paths:` allow-list: this is the skill's own hard refusal. Ask the user which directories may be scanned; never widen the list yourself to get past it.\n- An archive mount, takeout or bucket is unreadable (permission, expired token): report the exact path that failed and stop that source; do not fall back to scanning a parent directory.\n- Filing a find returns `write_pending` (exit 10) or `revision_conflict`: poll `gbrain write-request \u003Crequest_id>` for the first; re-read the page and merge for the second. Never re-file under a new slug.\n\n## Anti-Patterns\n\n- ❌ Running without `archive-crawler.scan_paths:` set. Hard refusal.\n  This is the safety contract — never bypass.\n- ❌ Hardcoding era-specific filing paths (e.g., `originals\u002Farchive\u002F`,\n  `originals\u002Fyc-era\u002F`). Read filing rules at runtime instead.\n- ❌ Re-showing items already marked in the manifest. The user's time\n  is the scarcest resource.\n- ❌ Paraphrasing reactions. Exact words only.\n- ❌ Wrapping found content in lessons or takeaways. Let stories breathe.\n- ❌ Skipping back-links when content references people \u002F companies who\n  have brain pages. Iron Law per conventions\u002Fquality.md.\n\n## Related skills\n\n- `skills\u002Fvoice-note-ingest\u002FSKILL.md` — same exact-phrasing pattern for\n  audio capture\n- `skills\u002Fidea-ingest\u002FSKILL.md` — single-link-or-article ingest with\n  the same primary-subject filing rule\n- `skills\u002Fconventions\u002Fquality.md` — citations, back-links, voice\n\n\n## Contract\n\nThis skill guarantees:\n\n- Routing matches the canonical triggers in the frontmatter.\n- Output written under the directories listed in `writes_to:` (when applicable).\n- Conventions referenced (`quality.md`, `brain-first.md`, `_brain-filing-rules.md`) are followed.\n- Privacy contract preserved: no real names, no fork-specific filesystem path literals, no upstream-fork references.\n\nThe full behavior contract is documented in the body sections above; this section exists for the conformance test.\n\n## Output Format\n\nThe skill's output shape is documented inline in the body sections above (see \"Output\", \"Brain page format\", or equivalent). The literal section header here exists for the conformance test (`test\u002Fskills-conformance.test.ts`).\n","# archive-crawler — The Universal Archivist\n\n> **Convention:** see [conventions\u002Fquality.md](..\u002Fconventions\u002Fquality.md) for\n> citation rules, exact-phrasing requirements when capturing the user's\n> reactions, and back-link enforcement.\n>\n> **Convention:** see [_brain-filing-rules.md](..\u002F_brain-filing-rules.md) —\n> this skill is **schema-generic**: it reads the user's filing rules from\n> the rules JSON instead of hardcoding any specific era \u002F archive layout.\n\n## Safety gate (REQUIRED, no exceptions)\n\narchive-crawler refuses to run unless `archive-crawler.scan_paths:` is\nexplicitly set in `gbrain.yml`. This is a deliberate safety fence against\nthe agent over-scoping a scan and ingesting sensitive content (tax PDFs,\nmedical records, credentials).\n\n```yaml\n# gbrain.yml — the allow-list is mandatory\narchive-crawler:\n  scan_paths:\n    - ~\u002FDocuments\u002Fwriting\u002F\n    - ~\u002FDropbox\u002FArchive\u002F\n    - \u002Fmnt\u002Fbackup\u002Fold-letters\u002F\n  # Optional deny-list inside the allow-list:\n  # deny_paths:\n  #   - ~\u002FDocuments\u002Ffinances\u002F\n  #   - ~\u002FDocuments\u002Fmedical\u002F\n```\n\nIf `scan_paths` is empty or missing, the skill exits with:\n\n```\narchive-crawler: refusing to run. No `archive-crawler.scan_paths:` allow-list\nin gbrain.yml. Add explicit paths the agent is permitted to scan, then re-run.\nThis is a safety fence — the agent will not infer what's safe to read.\n```\n\nThis contract is enforced by `src\u002Fcore\u002Fstorage-config.ts` (mirrors the\n`db_tracked` \u002F `db_only` allow-list pattern from v0.22.11 storage tiering).\n\n## What this is\n\nGeneric engine for exploring any tree of personal content within an\nexplicit allow-list. Works on local mounts, Dropbox API targets,\nBackblaze B2, Gmail takeouts (`.mbox`), and similar archives. Filters\nfor \"gold\" (the user's own writing, ideas, relationships) and surfaces\nit interactively for review. Skips noise (system files, configs, binary\nblobs).\n\n## Concepts\n\n### Source\n\nA source is any tree of files to explore. Sources have:\n\n- **type**: `local` | `dropbox` | `backblaze` | `gmail-takeout` | `mbox` | `pst`\n- **root**: filesystem path, Dropbox path, B2 prefix, mbox path\n- **manifest**: a brain page tracking progress at\n  `projects\u002F\u003Carchive-slug>\u002FSTATUS.md`\n\n### Manifest\n\nEvery archive exploration gets a manifest brain page that tracks:\n\n1. **Tree inventory** — folders \u002F files \u002F sizes \u002F types\n2. **Triage status** — each item: `⬜ unseen` \u002F `? reviewed` \u002F\n   `✅ ingested` \u002F `⏭️ skip` \u002F `? high-signal`\n3. **User reactions** — exact quotes when they react (per\n   conventions\u002Fquality.md exact-phrasing rule)\n4. **Priority queue** — what to explore next, ranked\n5. **Session log** — timestamped record of what was shown per session\n\n### Gold filter\n\nBefore showing anything to the user, apply the gold filter:\n\n| Keep (show) | Skip (note existence, don't show) |\n|-------------|-----------------------------------|\n| Personal writing (journals, letters, reflections, essays) | System files, configs, package.json, node_modules |\n| Conversations (IM logs, email threads with substance) | Binary blobs (images \u002F video) |\n| Ideas, theses, frameworks | Receipts, invoices, tax docs |\n| Relationship material (letters to \u002F from people who matter) | Spam, newsletters, mailing-list bulk |\n| Creative work (poetry, stories, code with soul) | Corrupted \u002F null files |\n| Origin stories (first versions of things that became important) | |\n| Emotional content (anger, love, grief, discovery) | |\n\n## Protocol\n\n### Phase 1: Inventory\n\nWhen pointed at a new source:\n\n1. **Confirm scan_paths is set** (safety gate). Exit if not.\n2. **Map the tree** — list folders + files + sizes + date ranges.\n3. **Classify folders** — group by likely content type (writing, email,\n   code, photos, docs, system).\n4. **Create manifest** — write `projects\u002F\u003Carchive-slug>\u002FSTATUS.md` with\n   the full inventory.\n5. **Propose priority queue** — rank folders by likely gold density.\n6. **Present to user** — show the map and proposed order. Let them\n   override.\n\n### Phase 2: Crawl\n\nWork through folders in priority order:\n\n1. **Read before showing** — open each candidate file, apply the gold\n   filter, skip noise.\n2. **Show one at a time** — present gold items individually for review.\n3. **Capture exact reaction** — track the user's response in the\n   manifest using their exact words (per conventions\u002Fquality.md).\n4. **Ingest if worth keeping** — create a brain page immediately.\n5. **Update manifest** — mark item status after each interaction.\n6. **Never re-show** — check the manifest before presenting anything.\n\n### Phase 3: Ingest\n\nWhen an item is worth keeping, file it by **primary subject** per\n`_brain-filing-rules.md`:\n\n- User's own writing \u002F ideas \u002F origin-story content → `originals\u002F\u003Cslug>.md`\n- Reflections \u002F personal-life content → `personal\u002F\u003Cslug>.md`\n- Product \u002F business ideas → `ideas\u002F\u003Cslug>.md`\n- Letters or threads about a specific person → `people\u002F\u003Cperson>\u002Ftimeline`\n  back-link plus the letter at `personal\u002F\u003Cslug>.md` or `originals\u002F\u003Cslug>.md`\n\n**The skill is schema-generic.** It does NOT bake in any specific\nera-folder structure (e.g., `originals\u002Farchive\u002F` for pre-2003,\n`originals\u002Fyc-era\u002F` for post-2019, etc.). The user's filing rules from\n`_brain-filing-rules.json` are read at runtime; the agent decides per-page\nwhere content lands within those sanctioned directories.\n\nBrain page format:\n\n```markdown\n---\ntitle: \"[Title or first line]\"\ntype: original\nsource_type: \"[local|dropbox|backblaze|gmail-takeout|mbox|pst]\"\nsource_path: \"[path within the allow-listed scan_paths]\"\ndate: \"YYYY-MM-DD\"  # date from the file metadata or content\npeople: [\"person-1\", \"person-2\"]\ntags: [\"tag-1\", \"tag-2\"]\n---\n\n# [Title]\n\n[Summary: what it is, when it's from, why it matters]\n\n**User's reaction:** [exact quote, no paraphrasing]\n\n## Context\n\n[Cross-links to people, concepts, projects.]\n\n---\n\n[Raw source material below the line — full text]\n```\n\n## File-type handlers\n\n### Plain text \u002F HTML \u002F Markdown\nRead directly. Strip HTML tags for display.\n\n### `.mbox` (email archives)\n\n```python\nimport mailbox\nmbox = mailbox.mbox('\u002Fpath\u002Fto\u002Ffile.mbox')\nfor msg in mbox:\n    body = ''\n    if msg.is_multipart():\n        for part in msg.walk():\n            if part.get_content_type() == 'text\u002Fplain':\n                body = part.get_payload(decode=True).decode('utf-8', errors='replace')\n                break\n    else:\n        body = msg.get_payload(decode=True).decode('utf-8', errors='replace')\n    # Apply gold filter\n```\n\n### `.doc` \u002F `.docx`\n\n```bash\n# .docx (modern)\npython3 -c \"\nimport zipfile, xml.etree.ElementTree as ET\nwith zipfile.ZipFile('\u002Fpath\u002Fto\u002Ffile.docx') as z:\n    tree = ET.parse(z.open('word\u002Fdocument.xml'))\n    print(''.join(t.text or '' for t in tree.iter('{http:\u002F\u002Fschemas.openxmlformats.org\u002Fwordprocessingml\u002F2006\u002Fmain}t')))\n\"\n\n# .doc (legacy, requires antiword or catdoc)\nantiword \u002Fpath\u002Fto\u002Ffile.doc 2>\u002Fdev\u002Fnull || catdoc \u002Fpath\u002Fto\u002Ffile.doc 2>\u002Fdev\u002Fnull\n```\n\n### `.pst` (Outlook archives)\n\n```bash\n# Validate first; many PSTs are null bytes\npython3 -c \"\nwith open('\u002Fpath\u002Fto\u002Ffile.pst', 'rb') as f:\n    print('Valid PST' if f.read(4) == b'!BDN' else 'CORRUPT\u002FNULL')\n\"\n# If valid:\nreadpst -o \u002Ftmp\u002Fpst-output \u002Fpath\u002Fto\u002Ffile.pst\n```\n\n### `.zip` \u002F `.tar` \u002F `.tar.gz`\n\nExtract to a temp dir, then recurse through the extracted tree.\n\n### Images\n\nNote existence + metadata (filename, size, date). Don't show unless the\nuser asks. Flag scans \u002F portraits as potentially personal.\n\n## Manifest template\n\n```markdown\n---\ntitle: \"[Archive Name] — Ingestion Status\"\ntype: project\ncreated: YYYY-MM-DD\nupdated: YYYY-MM-DD\nsource_type: \"[local|dropbox|...]\"\nscan_paths: [\"paths from gbrain.yml\"]\n---\n\n# [Archive Name] — Ingestion Status\n\n## Source\n- **Type:** [local|dropbox|...]\n- **Allow-listed paths:** [from gbrain.yml]\n- **Total files:** [N]\n- **Total size:** [X GB]\n- **Date range:** [earliest] — [latest]\n\n## Inventory\n\n### [Folder 1]\n| Item | Type | Size | Status | Reaction |\n|------|------|------|--------|----------|\n| file1.txt | text | 2KB | ✅ ingested | ? \"exact quote\" |\n| file2.doc | doc | 15KB | ⏭️ skip | — |\n| file3.html | html | 4KB | ⬜ unseen | — |\n\n### [Folder 2]\n...\n\n## Priority Queue\n1. [Highest priority — why]\n2. [Next — why]\n...\n\n## Session Log\n\n### YYYY-MM-DD — [Session topic]\n- Reviewed: [list]\n- Reactions: [exact quotes]\n- Ingested: [brain pages created]\n- Next: [what's queued]\n```\n\n## When it fails\n\nFollow the [agent operator protocol](..\u002Fconventions\u002Fagent-operator-protocol.md) for any gbrain error `code`, exit code, `[AGENT]` block or notice block. Specific to this skill:\n\n- No `archive-crawler.scan_paths:` allow-list: this is the skill's own hard refusal. Ask the user which directories may be scanned; never widen the list yourself to get past it.\n- An archive mount, takeout or bucket is unreadable (permission, expired token): report the exact path that failed and stop that source; do not fall back to scanning a parent directory.\n- Filing a find returns `write_pending` (exit 10) or `revision_conflict`: poll `gbrain write-request \u003Crequest_id>` for the first; re-read the page and merge for the second. Never re-file under a new slug.\n\n## Anti-Patterns\n\n- ❌ Running without `archive-crawler.scan_paths:` set. Hard refusal.\n  This is the safety contract — never bypass.\n- ❌ Hardcoding era-specific filing paths (e.g., `originals\u002Farchive\u002F`,\n  `originals\u002Fyc-era\u002F`). Read filing rules at runtime instead.\n- ❌ Re-showing items already marked in the manifest. The user's time\n  is the scarcest resource.\n- ❌ Paraphrasing reactions. Exact words only.\n- ❌ Wrapping found content in lessons or takeaways. Let stories breathe.\n- ❌ Skipping back-links when content references people \u002F companies who\n  have brain pages. Iron Law per conventions\u002Fquality.md.\n\n## Related skills\n\n- `skills\u002Fvoice-note-ingest\u002FSKILL.md` — same exact-phrasing pattern for\n  audio capture\n- `skills\u002Fidea-ingest\u002FSKILL.md` — single-link-or-article ingest with\n  the same primary-subject filing rule\n- `skills\u002Fconventions\u002Fquality.md` — citations, back-links, voice\n\n\n## Contract\n\nThis skill guarantees:\n\n- Routing matches the canonical triggers in the frontmatter.\n- Output written under the directories listed in `writes_to:` (when applicable).\n- Conventions referenced (`quality.md`, `brain-first.md`, `_brain-filing-rules.md`) are followed.\n- Privacy contract preserved: no real names, no fork-specific filesystem path literals, no upstream-fork references.\n\nThe full behavior contract is documented in the body sections above; this section exists for the conformance test.\n\n## Output Format\n\nThe skill's output shape is documented inline in the body sections above (see \"Output\", \"Brain page format\", or equivalent). The literal section header here exists for the conformance test (`test\u002Fskills-conformance.test.ts`).\n",{"name":5,"version":50,"mutating":51,"triggers":52,"writes_to":57,"description":7,"when_to_use":61,"writes_pages":51},"0.1.0",true,[53,54,24,55,56],"crawl my archive","find gold in my archive","scan my dropbox for","mine my old files for",[58,59,60],"originals\u002F","personal\u002F","ideas\u002F","Use when the user asks: \"crawl my archive\", \"find gold in my archive\", \"archive crawler\", \"scan my dropbox for\", \"mine my old files for\".","master","074ec301549ded4930f04d2c3b22d709a6c095cc","plugin\u002Fskills\u002Farchive-crawler",4842,"https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain","https:\u002F\u002Fgithub.com\u002Fgarrytan","https:\u002F\u002Fblog.garrytan.com","Writes software, dreams. ","2026-11-11 10:24:08",{"source":8,"skillName":5,"commands":72,"prompt":82,"zipAvailable":51},[73,76,79],{"tool":74,"command":75},"npx","npx skills add garrytan\u002Fgbrain --skill archive-crawler",{"tool":77,"command":78},"bunx","bunx skills add garrytan\u002Fgbrain --skill archive-crawler",{"tool":80,"command":81},"pnpm","pnpm dlx skills add garrytan\u002Fgbrain --skill archive-crawler","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Fplugin\u002Fskills\u002Farchive-crawler\u002FSKILL.md",[84],{"path":85,"resourceType":86,"resourceTypeDesc":87,"sizeBytes":88,"sourceHtmlUrl":89},"routing-eval.jsonl","OTHER","其他",666,"https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Fplugin\u002Fskills\u002Farchive-crawler\u002Frouting-eval.jsonl",[91,104,110,133,158,171],{"skillNo":92,"name":93,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":96,"publishedAt":45},"01M6N109FNGH1PK49T734PZXGB","query-helper","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog\u002Fskills\u002Fquery-helper\u002FSKILL.md",[],[97,98,99,100,101,102,103],"query helper","helper","query","search","page","find","false",{"skillNo":105,"name":106,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":109,"publishedAt":45},"01M6N108A23GW6NGQEQ0E7GV0A","broken","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog\u002Fskills\u002Fbroken\u002FSKILL.md",[],[106],{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":116,"publishedAt":45},"01M6N105E88C5K2H03H0CC79G1","brain-ops","Read, enrich, and write brain pages with source attribution.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog\u002Fskills\u002Fbrain-ops\u002FSKILL.md",[],[117,118,119,120,121,101,122,123,124,125,99,126,127,128,129,130,131,132,100],"brain ops","brain","ops","true","attribution","read","write","up","look","pages","people","with","enrich","web","path","source",{"skillNo":134,"name":135,"nameZh":6,"description":136,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":137,"categories":138,"keywords":142,"publishedAt":45},"01M6N102GRMF22TVCNT6RK5MRD","meeting-prep","Build a briefing before you meet someone, from their recent threads, open loops and what they care about.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog-query\u002Fskills\u002Fmeeting-prep\u002FSKILL.md",[139],{"categoryNo":140,"categoryName":141,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000034","行政与办公人员",[143,144,145,146,147,100,148,149,150,99,103,151,152,153,154,155,156,157],"meeting","prep","meeting prep","me","prepare","brief","open","build","loops","about","you","briefing","threads","for","Meet",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":164,"publishedAt":45},"01M6N1005QK7REHX5KGMNR5PF0","inbox-zero","Triage the inbox into replies, delegations and archives.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog-query\u002Fskills\u002Finbox-zero\u002FSKILL.md",[],[165,166,167,100,168,103,169,31,170],"inbox zero","ZeRO","inbox","clear","replies","triage",{"skillNo":172,"name":173,"nameZh":6,"description":174,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":175,"categories":176,"keywords":177,"publishedAt":45},"01M6N0ZXQ8RRRW71PNV61T57GP","deal-memo","Draft an investment memo from the deal pages and the founder history already in the brain.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog-query\u002Fskills\u002Fdeal-memo\u002FSKILL.md",[],[178,179,180,126,118,181,182,123,183,120,100,184],"deal memo","deal","memo","draft","investment","history","founder"]