···11+# CLAUDE.md
22+33+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44+55+## Project Overview
66+77+leaflet-hugo-sync is a Go CLI tool that syncs Leaflet/Bluesky blog posts to Hugo-compatible markdown files. It fetches blog entries from the AT Protocol (ATProto) network and converts them to markdown with downloaded media.
88+99+## Build & Run Commands
1010+1111+### Using Nix (recommended)
1212+1313+```bash
1414+# Build with Nix
1515+nix build
1616+1717+# Run directly
1818+nix run . -- -config .leaflet-sync.yaml
1919+2020+# Enter development shell
2121+nix develop
2222+2323+# Within nix develop shell:
2424+go build -o leaflet-hugo-sync ./cmd/leaflet-hugo-sync
2525+go test ./...
2626+```
2727+2828+### Using Go directly
2929+3030+```bash
3131+# Build the binary
3232+go build -o leaflet-hugo-sync ./cmd/leaflet-hugo-sync
3333+3434+# Run directly
3535+go run ./cmd/leaflet-hugo-sync/main.go -config .leaflet-sync.yaml
3636+3737+# Run tests
3838+go test ./...
3939+4040+# Run specific package tests
4141+go test ./internal/config
4242+go test ./internal/generator
4343+```
4444+4545+## Architecture
4646+4747+### Core Data Flow
4848+4949+The application follows this pipeline:
5050+5151+1. **ATProto Resolution** (`internal/atproto/client.go`)
5252+ - Resolves Bluesky handle → DID using public resolver (bsky.social)
5353+ - Resolves DID → PDS endpoint via plc.directory
5454+ - Creates client pointing to user's personal data server
5555+5656+2. **Record Fetching** (`internal/atproto/client.go:FetchEntries`)
5757+ - Fetches all records from specified collection (e.g., `pub.leaflet.document`)
5858+ - Handles pagination using cursors
5959+ - Optionally filters by publication URI
6060+6161+3. **Content Conversion** (`internal/converter/markdown.go`)
6262+ - Converts Leaflet's block-based format to markdown
6363+ - Handles multiple block types: text, code, unorderedList, image, bskyPost
6464+ - Processes rich text facets (links, mentions, inline code)
6565+ - Returns markdown string + list of image references
6666+6767+4. **Media Download** (`internal/media/downloader.go`)
6868+ - Downloads blob images from PDS via `com.atproto.sync.getBlob` XRPC endpoint
6969+ - Determines file extension from Content-Type header
7070+ - Caches downloaded images (skips if file exists)
7171+ - Returns Hugo-compatible image paths
7272+7373+5. **Post Generation** (`internal/generator/hugo.go`)
7474+ - Uses Go templates to generate frontmatter from config
7575+ - Combines frontmatter + markdown content
7676+ - Writes final `.md` files to configured output directory
7777+7878+### Key Types
7979+8080+**ATProto Types** (`internal/atproto/types.go`):
8181+- `LeafletDocument`: Top-level document with title, publishedAt, pages
8282+- `Page`: Contains array of BlockWrappers
8383+- `BlockWrapper`: Wraps a block with deferred JSON unmarshaling
8484+- Block types: `TextBlock`, `CodeBlock`, `UnorderedListBlock`, `ImageBlock`, `BskyPostBlock`
8585+- `Facet`: Rich text annotation with byte-based ranges (links, mentions, inline code)
8686+8787+**Configuration** (`internal/config/config.go`):
8888+- `Source`: Specifies handle, collection, optional publication_name
8989+- `Output`: Defines posts_dir, images_dir, image_path_prefix
9090+- `Template`: Go template strings for frontmatter and content
9191+9292+### Important Implementation Details
9393+9494+**Facet Processing**: Facets use byte offsets, not rune offsets. The converter handles this by working with `[]byte` instead of string indices when applying rich text formatting.
9595+9696+**Collection Migration**: The code defaults `com.whtwnd.blog.entry` to `pub.leaflet.document` as the old collection is deprecated (see main.go:104-110).
9797+9898+**Blob Download**: Uses the pattern `{PDS_HOST}/xrpc/com.atproto.sync.getBlob?did={DID}&cid={CID}` to fetch images.
9999+100100+**Publication Filtering**: When `publication_name` is configured, the tool first fetches `pub.leaflet.publication` records, finds the matching publication URI, then filters documents by that URI.
101101+102102+## Configuration File
103103+104104+The tool requires a YAML config file (default: `.leaflet-sync.yaml`):
105105+106106+```yaml
107107+source:
108108+ handle: "username.bsky.social"
109109+ collection: "pub.leaflet.document"
110110+ publication_name: "optional-publication-name" # Filter by specific publication
111111+112112+output:
113113+ posts_dir: "content/posts/leaflet"
114114+ images_dir: "static/images/leaflet"
115115+ image_path_prefix: "/images/leaflet"
116116+117117+template:
118118+ frontmatter: |
119119+ ---
120120+ title: "{{ .Title }}"
121121+ date: {{ .CreatedAt }}
122122+ ---
123123+```
124124+125125+Template variables available: `.Title`, `.CreatedAt`, `.Slug`, `.Handle`, `.OriginalURL`, `.Content`
126126+127127+## Dependencies
128128+129129+- `github.com/bluesky-social/indigo`: Official ATProto/Bluesky Go library for XRPC client and ATProto methods
130130+- `gopkg.in/yaml.v3`: YAML config parsing