┌──────────────────────────────────────────────────────────────────┐
│ │
│ Your Downloads folder, unwrapped. │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Discover │ ──→ │ Plan │ ──→ │ Build │ ──→ HTML │
│ │ the data │ │ the │ │ the │ │
│ │ │ │ journey │ │ world │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ run script design map merge into │
│ write story write voice template │
│ │ │
│ Frognu │
│ sentinel │
│ watches & │
│ takes notes │
│ │
└──────────────────────────────────────────────────────────────────┘
Downloads Wrapped is Spotify Wrapped for your Downloads folder. Point it at any folder and Frognu — a small curious frog in a yarn-ball cart — drives through your files and tells you who you really are.
Built with Hankweave. This repo is also a walkthrough of how Hankweave works and why it works the way it does — every design decision below traces back to a philosophy.
This is a three-codon taster we put together to show the shape of a Hank. In production, Hankweave runs pipelines with 20+ codons, loops, multi-model orchestration, and runs stretching to 18+ hours. If this is what an afternoon Hank looks like, imagine what a serious one can do.
# Run on your Downloads folder
bunx hankweave ./hank.json ~/Downloads
# Or any folder
bunx hankweave ./hank.json ~/Desktop
# Validate first (catches problems before spending tokens)
bunx hankweave ./hank.json ~/Downloads --validateYou get back a single HTML file — an interactive experience with a world map, Frognu's dialogue, and a full dashboard of your download habits.
What you'll learn about yourself:
- Your download personality — Night Owl? Early Bird? Afternoon Warrior?
- Your busiest day ever — and Frognu's concerned reaction to it
- Your duplicate situation — devotion disguised as disorganization
- Your forgotten relics — files untouched for years
- Your crisis days — when you downloaded 30+ things and clearly weren't okay
This is a three-codon pipeline. Each codon does one thing, hands off files to the next, and gets out of the way.
┌─────────────────────────────────────────────────────────────┐
│ hank.json │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ DISCOVER │ │ PLAN │ │ BUILD │ │
│ │ (haiku) │──→│ (haiku) │──→│ (haiku) │ │
│ │ │ │ │ │ │ │
│ │ runs the │ │ designs the │ │ merges data │ │
│ │ analysis │ │ world map, │ │ into HTML │ │
│ │ script, │ │ writes │ │ template │ │
│ │ writes the │ │ Frognu's │ │ │ │
│ │ narrative │ │ voice │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ analysis- world.json downloads- │
│ summary.json dialogue wrapped.html │
│ understanding.md dashboardCommentary │
│ │
│ frognu-thoughts sentinel │
│ watches Plan, captures musings │
│ │
└─────────────────────────────────────────────────────────────┘
Model: haiku — this is mechanical work, cheap is fine.
The agent runs a TypeScript analysis script that's been copied into its workspace by the rig. The script scans every file in the folder and computes statistics, patterns, personality classification, crisis days, duplicates — all deterministically. The agent doesn't figure any of this out itself; it just runs the script and reads the output.
Then the agent writes notes/understanding.md — a prose narrative about what the script found. "March 15th was a big day for you. 47 downloads. Were you okay?" This is the agent's creative contribution: interpreting facts into story.
Files produced:
notes/data/analysis-summary.json— all the numbers (~20KB)notes/data/analysis-inventory.json— every file's metadata (~1MB+)notes/understanding.md— the story
Model: haiku — with a detailed enough prompt, haiku handles creative work well.
This is the creative heart. The agent reads the narrative and the numbers, then designs the world: zones representing file categories, landmarks for notable files, Frognu's dialogue for each zone, and dashboard commentary that brings the numbers to life with Frognu's voice.
A sentinel watches this codon. As the agent plans the world, the frognu-thoughts sentinel — running haiku with an 8-second debounce — captures Frognu's real-time musings. These get written to notes/narrative/frognu-musings.md and can enrich the final experience. The main agent doesn't know it's being observed.
Files produced:
notes/world/world.json— zones, files, dialogue, anddashboardCommentarynotes/world/narrative.md— the journey in prose (for debugging)notes/world/selection-log.md— why specific files were chosen
Model: haiku — pure assembly, no creativity needed.
Takes the numbers from the script and the creative text from Codon 2, merges them into a single worldData object, and injects it into the HTML template using a merge script. The agent never touches the template directly — the merge script handles placeholder substitution safely.
Files produced:
notes/world/worldData.json— the merged data objectoutput/downloads-wrapped.html— the final experience
Hankweave is built on a set of ideas about how agentic systems should work. This Hank embodies each one. Here's where to find them.
Not all parts of a problem need the same constraints. Some parts benefit from tight control — others benefit from letting the agent explore.
TIGHT LOOSE TIGHT
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Discover │ │ Plan │ │ Build │
│ │ │ │ │ │
│ Run this │ │ Design the │ │ Merge this │
│ script. │ │ world. Write│ │ data into │
│ Read the │ │ the voice. │ │ this │
│ output. │ │ Make it │ │ template. │
│ Write a │ │ personal. │ │ Run this │
│ narrative. │ │ │ │ script. │
└─────────────┘ └─────────────┘ └─────────────┘
deterministic creative deterministic
Codons 1 and 3 are tightly constrained — run a specific script, follow a specific schema, merge data a specific way. The agent has very little room to deviate. Codon 2 is the creative middle — freedom to design zones, choose which files to feature, write Frognu's dialogue, craft the emotional arc. The output structure is defined, but the content is entirely up to the agent.
This is why the pipeline is stable even though the creative output varies. The tight codons on either side act as guardrails.
See it in: codon-1-discover.md (tight instructions, single script), codon-2-plan-journey.md (open-ended creative brief), codon-3-build-world.md (mechanical merge steps).
Codon boundaries are hard walls. Problems in one codon don't leak into the next. If something goes wrong, you know where to look.
Each codon runs with continuationMode: "fresh". No accumulated context. No conversation history leaking between steps. When Codon 2 starts, it has fresh eyes — it reads files, not memories of what Codon 1 did.
This means when something goes wrong, the blast radius is contained:
| If this is wrong... | The problem is in... |
|---|---|
| Numbers are off | The analysis script (rig-files/scripts/analyze.ts) |
| Narrative is flat | Codon 1's prompt or the agent's prose |
| World layout is bad | Codon 2's zone design |
| Frognu sounds wrong | Codon 2's voice instructions |
| Dashboard shows fallbacks | Codon 3's merge (field name mismatch?) |
| HTML is broken | The template or the merge script |
A single-codon version would do everything in one long session. When the dashboard showed fallback text, you'd have no idea where to look. Three codons = three trenches, each with its own checkpoint you can roll back to.
See it in: hank.json — every codon uses "continuationMode": "fresh". Each prompt starts by telling the agent what files to read, not what happened before.
What if every character in every intermediate could be traced to the specific decision, response, source, or script that caused it?
Every step of this Hank produces inspectable artifacts:
Codon 1 produces:
analysis-summary.json ← script output, deterministic, verifiable
analysis-inventory.json ← full file listings, every file accounted for
understanding.md ← the agent's narrative, readable by humans
Codon 2 produces:
world.json ← zone definitions, file selections, dialogue, commentary
narrative.md ← the journey walkthrough, in prose
selection-log.md ← why each file was chosen
Sentinel produces:
frognu-musings.md ← what Frognu noticed in real time
Codon 3 produces:
worldData.json ← the merged object, inspectable before injection
downloads-wrapped.html ← the final output
Nothing is hidden. If the dashboard says "You're a Night Owl," you can trace it: worldData.json → dashboardCommentary.personalityDescription in world.json → written by the agent in Codon 2 → based on personality.type: "night-owl" in analysis-summary.json → computed by the analysis script from hour distribution data. Every character has a receipt.
See it in: checkpointedFiles in hank.json — each codon declares what files it tracks. The runtime snapshots them at every boundary.
The problem of leaving useful and appropriate intermediates for another intelligence.
How does Codon 2 know what Codon 1 found? Not through conversation — through files designed for the reader.
The analysis script writes two separate files: a ~20KB summary and a ~1MB+ inventory. Why split them? Because Codon 2 only needs the summary to design zones and write dialogue. Loading a megabyte of file metadata into context would waste tokens and might confuse the agent. The inventory exists for when the agent needs a specific filename — it reads selectively, not all at once.
And then there's understanding.md — the agent's prose narrative from Codon 1. This isn't data. It's a context bridge: observations written by one agent specifically so another agent can absorb the story quickly. Codon 2's prompt says "start with understanding.md for the feel, then read the summary for hard numbers." Different files for different purposes.
analysis-summary.json → numbers for the dashboard (consumed by Codon 2 & 3)
analysis-inventory.json → specific filenames (consumed selectively by Codon 2)
understanding.md → the human story (consumed by Codon 2 for tone)
See it in: codon-2-plan-journey.md opens with "Start with understanding.md for the feel, then read the summary for hard numbers. Only read the inventory if you need specific filenames."
Hankweave is amber. Your working AI workflow is the mosquito. The value of amber isn't the amber itself — it's what it preserves.
This Hank started as interactive sessions — talking to Claude about Downloads folders, seeing what worked, what voice landed, what visualizations made sense. The analyze.ts script was refined over multiple runs, accumulating edge cases: handling special characters in filenames, correctly classifying file types, computing personality from hour distributions.
All of that discovered behavior is now frozen into structure. The prompt for Frognu's voice didn't come from imagination — it came from observing what kind of humor actually worked across different folders. The zone design rules emerged from seeing what looked good on different data sets. The wrapped data format was shaped by what the HTML template actually needs.
The Hank preserves what worked. Someone else can run it — on their own folder, on a different machine — and get the same kind of result, without needing to know how we got here.
See it in: rig-files/scripts/analyze.ts (1200+ lines of accumulated edge cases), codon-2-plan-journey.md (Frognu's voice guide — specific examples refined from real output), the wrapped schema in codon-3-build-world.md (exact field names the template expects, discovered through trial and error).
A 50-codon Hank with thousand-line prompts might work once. A 5-codon Hank with focused prompts is easier to understand, debug, and improve.
Three codons. That's it.
The instinct is to create more: a codon for file scanning, a codon for statistics, a codon for personality classification, a codon for zone design, a codon for dialogue writing, a codon for file selection, a codon for merging, a codon for validation. That's eight codons, and each one needs prompts, rigs, file handoffs, debugging.
Instead, we trust the agent. Codon 1: run the script and write the story. Codon 2: design the entire world. Codon 3: put it all together. Each codon does one meaningful thing, not one tiny thing.
The heuristic: what would you hand off to a competent colleague? Not "classify these files, then count them, then sort them." Just "analyze this folder and tell me what's interesting."
See it in: hank.json — three entries in the hank array. Each prompt is a few hundred lines, not thousands.
Discovery happens interactively with coding agents. Hankweave preserves what works. Greenfield is where you discover what works. Brownfield is where you make it last.
This Hank exists because of CCEPL — Claude Code Eval Print Loop. We worked interactively with coding agents to analyze folders, experimenting with visualizations, trying different narrative voices, testing dashboard layouts. That was greenfield — exploration, discovery, dead ends.
Once we found what worked, we froze it. The analysis script was extracted from working sessions. Frognu's voice was distilled from dialogue that actually landed. The zone design rules were patterns that emerged from real data.
Now it's brownfield. When the script mishandles a file type, we fix the script and the fix travels to every future run. When a prompt produces flat dialogue, we refine the voice guide and it improves everywhere. The Hank accumulates wisdom — it gets better over time without being rebuilt from scratch.
See it in: The whole repo. This isn't a Hank that was designed top-down. It was discovered bottom-up and then frozen.
One more thing that ties it all together — the division of labor between deterministic script and creative agent:
SCRIPT (deterministic) AGENT (creative)
───────────────────── ────────────────
totalFiles: 847 "You're a Night Owl"
totalSize: "12.4 GB" "847 files and counting..."
personality.type: "night-owl" "67% after 8pm — the swamp
personality.peakHour: 23 comes alive after dark"
busiestDay.count: 47 "47 in one day. Were you okay?"
duplicates.count: 34 "34 copies. That's devotion."
streaks.longestStreak: 23 zone names, dialogue, arc
Numbers from the script. Words from the agent. The final codon overlays one onto the other. Neither could do the other's job well — the script can't be funny, and the agent shouldn't be trusted with arithmetic.
downloads-wrapped/
├── hank.json ← The Hank definition
├── prompts/
│ ├── system.md ← Global context (shared by all codons)
│ ├── codon-1-discover.md ← "Run the script, write the story"
│ ├── codon-2-plan-journey.md ← "Design the world, write the voice"
│ └── codon-3-build-world.md ← "Merge everything into HTML"
├── rig-files/
│ ├── scripts/
│ │ ├── analyze.ts ← File analysis script (~1200 lines)
│ │ └── merge-template.ts ← Template injection script
│ ├── frognu/ ← Frog sprite assets
│ ├── icons/ ← File type icons
│ └── assets/ ← Lilypad, music, ambient
├── templates/
│ └── swamp-template.html ← The interactive HTML experience
├── sentinels/
│ └── frognu-thoughts.sentinel.json ← Sentinel configuration
└── sentinel-prompts/
├── frognu-system.md ← Who Frognu is
└── frognu-user.md ← What the sentinel sees
If you're here to learn how Hankweave works, read in this order:
-
hank.json— the program. Three codons, their models, their rigs, their prompts. This is the whole pipeline in one file. -
prompts/system.md— the global system prompt. Every codon sees this. It sets the vibe: what Downloads Wrapped is, who Frognu is, the tone we want. -
prompts/codon-1-discover.md— the first codon's prompt. Notice how it tells the agent exactly what files exist (set up by rigs), what command to run, and what to write. Self-contained — no assumptions about prior context. -
rig-files/scripts/analyze.ts— the deterministic script. This is what a rig provides: work that doesn't need intelligence, removed from the agent's plate. -
prompts/codon-2-plan-journey.md— the creative codon. Longer prompt, more freedom, but still structured: clear output format, voice guidelines, specific examples. This is where "regions of freedom" lives. -
sentinels/frognu-thoughts.sentinel.json— a sentinel config. Note the debounce trigger (fires after 8 seconds of events), the haiku model (cheap observation), and the output path. Sentinels observe without interfering. -
prompts/codon-3-build-world.md— the assembly codon. Mechanical instructions: read these files, merge this way, run this script. Low creativity, high precision. The tight end of the tight-loose-tight pattern.
Look at the execution directory (~/.hankweave-executions/[id]/):
agentRoot/notes/data/— the script's raw output (receipts)agentRoot/notes/understanding.md— the agent's narrative (context bridge)agentRoot/notes/world/world.json— the world definition (creative output)agentRoot/notes/narrative/frognu-musings.md— sentinel output (observation).hankweave/state.json— execution state, costs, timing.hankweave/checkpoints/— git-based snapshots at each codon boundary (trenches)
Things to try next:
- Change Frognu's voice in
codon-2-plan-journey.md— the creative region of freedom - Swap
"model": "haiku"to"model": "sonnet"on the creative codon — more intelligence, same structure - Add a fourth codon that reviews and improves the narrative — an easy way to add a quality loop
- Add a cost-tracking sentinel alongside frognu-thoughts — sentinels compose
- Run with
--validateto see what Hankweave checks before executing - Read the Hankweave docs for the full picture
- Read about the philosophy: Antibrittle Agents
Built with Hankweave by the team at Southbridge.