snappy-corpus skill
list year? month?readread path offset?readsearch queryread$ npx snappy-skills install snappy-corpus
You are working inside Robert's content engine. Every published artifact (carousel, blog, DM, sales asset) must ultimately trace back to something Robert actually said in a Krisp transcript. This loader covers the rules. Read SKILL.md only if you need the full architecture rationale.
typescriptimport { listTranscripts, readTranscript, searchCorpus } from "../snappy-corpus/api.ts";
| Function | Purpose |
|---|---|
listTranscripts(year?, month?) |
List transcript .md files, optionally filtered by year/month |
readTranscript(path, offset?, limit?) |
Read a transcript in chunks (default first 200 lines) |
searchCorpus(query) |
Grep across all transcripts for a query string |
CLI:
bashnpx tsx ~/.claude/skills/snappy-corpus/api.ts list [year] [month]
npx tsx ~/.claude/skills/snappy-corpus/api.ts read <path> [offset] [--limit N]
npx tsx ~/.claude/skills/snappy-corpus/api.ts search <query>
~/.claude/corpus/krisp/YYYY/MM/DD-<slug>.md.nuggets.json suffix~/.claude/corpus/krisp/index.json -- { "meeting_id": "relative/path/to/file.md" }~/.claude/corpus/krisp/skipped.log~/.claude/corpus/usage.jsonlDO NOT mine ~/.claude/skills/snappy-course/data/krisp-teaching-by-person.json -- see warning header in that file. Its verbatim_quote fields contain host speech mis-attributed to guests (Jordan fabrication root cause, pass-log 2026-04-13). Use quote-bank.json instead.
mcp__claude_ai_Krisp__search_meetings -- list meetings OR fetch metadata. Always restrict fields to the cheap set: ["name", "date", "attendees", "transcript"]. Returns inline (small). This is the ONLY way to get meeting name/date/attendees for frontmatter -- get_multiple_documents only returns {id, document}.mcp__claude_ai_Krisp__get_multiple_documents -- full doc by id. Hard cap: 10 ids per call.transcript.status != "uploaded". Log to skipped.log with reason.A 10-id get_multiple_documents batch is ~500KB / ~140k tokens. It will ALWAYS exceed the inline response limit and auto-spill to a file at ~/.claude/projects/<session>/tool-results/mcp-claude_ai_Krisp-get_multiple_documents-<ts>.txt. You cannot read this file via Read (too big) and you must NOT try to load it into context.
The only working pattern is Bash + Python:
get_multiple_documents -- you get back an error-style message with the spill file path.Bash to run python3 ~/.claude/skills/snappy-corpus/scripts/persist-batch.py <spill_file> <metadata_json>.index.json.This means ingest only works in contexts where Bash + python3 are available. Subagents with Bash denied CANNOT do ingest -- they must be dispatched with Bash permissions, or ingest must run in the parent context. If your sandbox blocks Bash, stop and report -- do not attempt to read the spill file.
Build a metadata JSON file BEFORE calling get_multiple_documents, using the cheap search_meetings call:
json{
"019d683e...": {"name": "...", "date": "2026-04-07T10:00:18-04:00", "attendees": ["Robert Boulos"]},
...
}
index.json -- if meeting_id already present, skip.~/.claude/corpus/krisp/YYYY/MM/DD-<slug>.md from the meeting date.type from name + attendees: dev-sync | mastermind | customer | prospect | sales | team | solo | unknown. Use heuristics: "Mastermind" → mastermind, names with @snappy.ai → team, solo recordings (no other attendees) → solo, etc. When uncertain → unknown.YYYY/MM/DD-<slug>.md path (happens with two same-named recurring meetings on the same day, e.g. "State Change Mastermind" twice), append -<last-6-of-meeting-id> to the slug. persist-batch.py already does this automatically.index.json with the new entry.get_multiple_documents with up to 10 ids at a time. One call per batch, not one call per meeting.attendees: [Robert Boulos] only → solomasterminddev-syncprospect or salesunknown (do not guess; mining can re-classify later)get_multiple_documents with more than 10 ids per call.Report:
If this AGENTS.md does not cover the case you are working on and you had to read other files to find the answer, before you finish, append one line to ~/.claude/logs/agents-md-feedback.log via Bash:
bashecho "[$(date -u +%FT%TZ)] snappy-corpus: <what was missing in the loader>" >> ~/.claude/logs/agents-md-feedback.log
One line per gap. Be specific.
<!-- SKILL-INDEX-START -->
[snappy-corpus Index]|root: ~/.claude/skills/snappy-corpus|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md}
<!-- SKILL-INDEX-END -->
snappy-course<!-- SNAPPY-CONTRACT-VERBS-START -->
Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.
| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
list |
year?, month? |
read |
npx tsx ~/.claude/skills/snappy-corpus/api.ts list |
read |
path, offset? |
read |
npx tsx ~/.claude/skills/snappy-corpus/api.ts read <path> |
search |
query |
read |
npx tsx ~/.claude/skills/snappy-corpus/api.ts search "<query>" |
When an answer carries face_hint, show it with one snappy_present(<answer>) call.
See /snappy-faces for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->
---
name: snappy-corpus
role: Loader for the Krisp transcript corpus + nugget pipeline. Use when ingesting transcripts, extracting nuggets, or mining the corpus for content.
loaded-by: PreToolUse hook (auto-injected when "snappy-corpus" is mentioned)
Triggers on: Krisp transcripts, corpus ingest, meetings, nuggets, transcript search
---
# snappy-corpus -- loader
You are working inside Robert's content engine. Every published artifact (carousel, blog, DM, sales asset) must ultimately trace back to something Robert actually said in a Krisp transcript. This loader covers the rules. Read SKILL.md only if you need the full architecture rationale.
## API module
```typescript
import { listTranscripts, readTranscript, searchCorpus } from "../snappy-corpus/api.ts";
```
| Function | Purpose |
|----------|---------|
| `listTranscripts(year?, month?)` | List transcript .md files, optionally filtered by year/month |
| `readTranscript(path, offset?, limit?)` | Read a transcript in chunks (default first 200 lines) |
| `searchCorpus(query)` | Grep across all transcripts for a query string |
CLI:
```bash
npx tsx ~/.claude/skills/snappy-corpus/api.ts list [year] [month]
npx tsx ~/.claude/skills/snappy-corpus/api.ts read <path> [offset] [--limit N]
npx tsx ~/.claude/skills/snappy-corpus/api.ts search <query>
```
## Storage paths (memorize)
- Transcripts: `~/.claude/corpus/krisp/YYYY/MM/DD-<slug>.md`
- Nuggets sidecar: same path with `.nuggets.json` suffix
- Dedup index: `~/.claude/corpus/krisp/index.json` -- `{ "meeting_id": "relative/path/to/file.md" }`
- Skipped log: `~/.claude/corpus/krisp/skipped.log`
- Usage ledger: `~/.claude/corpus/usage.jsonl`
**DO NOT mine `~/.claude/skills/snappy-course/data/krisp-teaching-by-person.json`** -- see warning header in that file. Its `verbatim_quote` fields contain host speech mis-attributed to guests (Jordan fabrication root cause, pass-log 2026-04-13). Use `quote-bank.json` instead.
## Krisp MCP -- the only ingest path
- `mcp__claude_ai_Krisp__search_meetings` -- list meetings OR fetch metadata. Always restrict `fields` to the cheap set: `["name", "date", "attendees", "transcript"]`. Returns inline (small). **This is the ONLY way to get meeting name/date/attendees for frontmatter** -- `get_multiple_documents` only returns `{id, document}`.
- `mcp__claude_ai_Krisp__get_multiple_documents` -- full doc by id. **Hard cap: 10 ids per call.**
- Skip any meeting where `transcript.status != "uploaded"`. Log to `skipped.log` with reason.
### CRITICAL: responses spill to tool-results files
A 10-id `get_multiple_documents` batch is ~500KB / ~140k tokens. **It will ALWAYS exceed the inline response limit and auto-spill to a file at `~/.claude/projects/<session>/tool-results/mcp-claude_ai_Krisp-get_multiple_documents-<ts>.txt`.** You cannot read this file via `Read` (too big) and you must NOT try to load it into context.
**The only working pattern is Bash + Python:**
1. Call `get_multiple_documents` -- you get back an error-style message with the spill file path.
2. Call `Bash` to run `python3 ~/.claude/skills/snappy-corpus/scripts/persist-batch.py <spill_file> <metadata_json>`.
3. The script parses the spill, writes each transcript as markdown with frontmatter, updates `index.json`.
**This means ingest only works in contexts where Bash + python3 are available.** Subagents with Bash denied CANNOT do ingest -- they must be dispatched with Bash permissions, or ingest must run in the parent context. If your sandbox blocks Bash, stop and report -- do not attempt to read the spill file.
### Metadata JSON shape (input to persist-batch.py)
Build a metadata JSON file BEFORE calling get_multiple_documents, using the cheap search_meetings call:
```json
{
"019d683e...": {"name": "...", "date": "2026-04-07T10:00:18-04:00", "attendees": ["Robert Boulos"]},
...
}
```
## Ingest playbook (one transcript)
1. Check `index.json` -- if `meeting_id` already present, skip.
2. Slugify the meeting name: lowercase, alphanumerics + hyphens, max 60 chars. Strip emojis and special chars.
3. Build path: `~/.claude/corpus/krisp/YYYY/MM/DD-<slug>.md` from the meeting `date`.
4. Classify `type` from name + attendees: `dev-sync` | `mastermind` | `customer` | `prospect` | `sales` | `team` | `solo` | `unknown`. Use heuristics: "Mastermind" → mastermind, names with @snappy.ai → team, solo recordings (no other attendees) → solo, etc. When uncertain → `unknown`.
- **Collision-proof**: if a different meeting_id already wrote to the same `YYYY/MM/DD-<slug>.md` path (happens with two same-named recurring meetings on the same day, e.g. "State Change Mastermind" twice), append `-<last-6-of-meeting-id>` to the slug. `persist-batch.py` already does this automatically.
5. Write the file with YAML frontmatter (see SKILL.md for exact shape) followed by the Krisp document body verbatim.
6. Update `index.json` with the new entry.
## Batching
- Always use `get_multiple_documents` with up to 10 ids at a time. One call per batch, not one call per meeting.
- Between batches, write files immediately -- do not buffer all results in memory.
## Frontmatter classification rules
- `attendees: [Robert Boulos]` only → `solo`
- Name contains "Mastermind" → `mastermind`
- Name contains "Sync", "Dev", "Working Session" + internal team member → `dev-sync`
- Name contains "Call", "Discovery", "Demo", external email domain → `prospect` or `sales`
- Default fallback → `unknown` (do not guess; mining can re-classify later)
## Rules
- Do not summarize, edit, or "clean up" the transcript body. It is the receipt. Persist verbatim.
- Do not write nuggets during ingest. Nugget extraction is a separate pass.
- Do not delete or rewrite existing transcript files. Idempotent re-runs only add new ones.
- Do not call `get_multiple_documents` with more than 10 ids per call.
- Do not auto-publish anything. Ever. Mining and ranking are fine; publishing is gated on Robert.
## When you finish an ingest run
Report:
- N meetings discovered in window
- N already in index (skipped)
- N successfully written
- N skipped due to transcript status (with reason)
- Total bytes written
- Path to a sample file so Robert can spot-check
---
## How to report loader gaps (self-healing feedback)
If this AGENTS.md does not cover the case you are working on and you had to read other files to find the answer, **before you finish**, append one line to `~/.claude/logs/agents-md-feedback.log` via Bash:
```bash
echo "[$(date -u +%FT%TZ)] snappy-corpus: <what was missing in the loader>" >> ~/.claude/logs/agents-md-feedback.log
```
One line per gap. Be specific.
<!-- SKILL-INDEX-START -->
[snappy-corpus Index]|root: ~/.claude/skills/snappy-corpus|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md}
<!-- SKILL-INDEX-END -->
## Used by
- `snappy-course`
<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs
Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.
| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
| `list` | `year?`, `month?` | `read` | `npx tsx ~/.claude/skills/snappy-corpus/api.ts list` |
| `read` | `path`, `offset?` | `read` | `npx tsx ~/.claude/skills/snappy-corpus/api.ts read <path>` |
| `search` | `query` | `read` | `npx tsx ~/.claude/skills/snappy-corpus/api.ts search "<query>"` |
## Show the result
When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->