···11+## [0.3.3.] - 2026-02-04
22+33+### โ๏ธ Miscellaneous Tasks
44+55+- Cleaned up remaining auth implementations
66+- Format
77+88+## [0.3.2] - 2026-02-05
99+1010+### ๐ Bug Fixes
1111+1212+- Fixed issue with auth selection in init command
1313+1414+### โ๏ธ Miscellaneous Tasks
1515+1616+- Release 0.3.2
1717+## [0.3.1] - 2026-02-04
1818+1919+### ๐ Bug Fixes
2020+2121+- Asset subdirectories
2222+2323+### โ๏ธ Miscellaneous Tasks
2424+2525+- Updated authentication ux
2626+- Release 0.3.1
2727+- Bumped version
2828+## [0.3.0] - 2026-02-04
2929+3030+### ๐ Features
3131+3232+- Initial oauth implementation
3333+- Add stripDatePrefix option for Jekyll-style filenames
3434+- Add `update` command
3535+3636+### โ๏ธ Miscellaneous Tasks
3737+3838+- Update changelog
3939+- Added workflows
4040+- Updated workflows
4141+- Updated workflows
4242+- Cleaned up types
4343+- Updated icon styles
4444+- Updated og image
4545+- Updated docs
4646+- Docs updates
4747+- Bumped version
4848+## [0.2.1] - 2026-02-02
4949+5050+### โ๏ธ Miscellaneous Tasks
5151+5252+- Added CHANGELOG
5353+- Merge main into chore/fronmatter-config-updates
5454+- Added linting and formatting
5555+- Linting updates
5656+- Refactored to use fallback approach if frontmatter.slugField is provided or not
5757+- Version bump
158## [0.2.0] - 2026-02-01
259360### ๐ Features
···764865### โ๏ธ Miscellaneous Tasks
9666767+- Resolved action items from issue #3
6868+- Adjusted tags to accept yaml multiline arrays for tags
6969+- Updated inject to handle new slug options
7070+- Updated comments
1071- Update blog post
1172- Fix blog build error
1273- Adjust blog post
+85
CLAUDE.md
···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+Sequoia is a CLI tool for publishing Markdown documents with frontmatter to the AT Protocol (Bluesky's decentralized social network). It converts blog posts into ATProto records (`site.standard.document`, `space.litenote.note`) and publishes them to a user's PDS.
88+99+Website: <https://sequoia.pub>
1010+1111+## Monorepo Structure
1212+1313+- **`packages/cli/`** โ Main CLI package (the core product)
1414+- **`docs/`** โ Documentation website (Vocs-based, deployed to Cloudflare Pages)
1515+1616+Bun workspaces manage the monorepo.
1717+1818+## Commands
1919+2020+```bash
2121+# Build CLI
2222+bun run build:cli
2323+2424+# Run CLI in dev (build + link)
2525+cd packages/cli && bun run dev
2626+2727+# Run tests
2828+bun run test:cli
2929+3030+# Run a single test file
3131+cd packages/cli && bun test src/lib/markdown.test.ts
3232+3333+# Lint (auto-fix)
3434+cd packages/cli && bun run lint
3535+3636+# Format (auto-fix)
3737+cd packages/cli && bun run format
3838+3939+# Docs dev server
4040+bun run dev:docs
4141+```
4242+4343+## Architecture
4444+4545+**Entry point:** `packages/cli/src/index.ts` โ Uses `cmd-ts` for type-safe subcommand routing.
4646+4747+**Commands** (`src/commands/`):
4848+4949+- `publish` โ Core workflow: scans markdown files, publishes to ATProto
5050+- `sync` โ Fetches published records state from ATProto
5151+- `update` โ Updates existing records
5252+- `auth` โ Multi-identity management (app-password + OAuth)
5353+- `init` โ Interactive config setup
5454+- `inject` โ Injects verification links into static HTML output
5555+- `login` โ Legacy auth (deprecated)
5656+5757+**Libraries** (`src/lib/`):
5858+5959+- `atproto.ts` โ ATProto API wrapper (two client types: AtpAgent for app-password, OAuth client)
6060+- `config.ts` โ Loads `sequoia.json` config and `.sequoia-state.json` state files
6161+- `credentials.ts` โ Multi-identity credential storage at `~/.config/sequoia/credentials.json` (0o600 permissions)
6262+- `markdown.ts` โ Frontmatter parsing (YAML/TOML), content hashing, atUri injection
6363+6464+**Extensions** (`src/extensions/`):
6565+6666+- `litenote.ts` โ Creates `space.litenote.note` records with embedded images
6767+6868+## Key Patterns
6969+7070+- **Config resolution:** `sequoia.json` is found by searching up the directory tree
7171+- **Frontmatter formats:** YAML (`---`), TOML (`+++`), and alternative (`***`) delimiters
7272+- **Credential types:** App-password (PDS URL + identifier + password) and OAuth (DID + handle)
7373+- **Build:** `bun build src/index.ts --target node --outdir dist`
7474+7575+## Tooling
7676+7777+- **Runtime/bundler:** Bun
7878+- **Linter/formatter:** Biome (tabs, double quotes)
7979+- **Test runner:** Bun's native test runner
8080+- **CLI framework:** `cmd-ts`
8181+- **Interactive UI:** `@clack/prompts`
8282+8383+## Git Conventions
8484+8585+Never add 'Co-authored-by' lines to git commits unless explicitly asked.
+81
action.yml
···11+name: 'Sequoia Publish'
22+description: 'Publish your markdown content to ATProtocol using Sequoia CLI'
33+branding:
44+ icon: 'upload-cloud'
55+ color: 'green'
66+77+inputs:
88+ identifier:
99+ description: 'ATProto handle or DID (e.g. yourname.bsky.social)'
1010+ required: true
1111+ app-password:
1212+ description: 'ATProto app password'
1313+ required: true
1414+ pds-url:
1515+ description: 'PDS URL (defaults to https://bsky.social)'
1616+ required: false
1717+ default: 'https://bsky.social'
1818+ force:
1919+ description: 'Force publish all posts, ignoring change detection'
2020+ required: false
2121+ default: 'false'
2222+ commit-back:
2323+ description: 'Commit updated frontmatter and state file back to the repo'
2424+ required: false
2525+ default: 'true'
2626+ working-directory:
2727+ description: 'Directory containing sequoia.json (defaults to repo root)'
2828+ required: false
2929+ default: '.'
3030+3131+runs:
3232+ using: 'composite'
3333+ steps:
3434+ - name: Setup Bun
3535+ uses: oven-sh/setup-bun@v2
3636+3737+ - name: Build and install Sequoia CLI
3838+ shell: bash
3939+ run: |
4040+ cd ${{ github.action_path }}
4141+ bun install
4242+ bun run build:cli
4343+ bun link --cwd packages/cli
4444+4545+ - name: Sync state from ATProtocol
4646+ shell: bash
4747+ working-directory: ${{ inputs.working-directory }}
4848+ env:
4949+ ATP_IDENTIFIER: ${{ inputs.identifier }}
5050+ ATP_APP_PASSWORD: ${{ inputs.app-password }}
5151+ PDS_URL: ${{ inputs.pds-url }}
5252+ run: sequoia sync
5353+5454+ - name: Publish
5555+ shell: bash
5656+ working-directory: ${{ inputs.working-directory }}
5757+ env:
5858+ ATP_IDENTIFIER: ${{ inputs.identifier }}
5959+ ATP_APP_PASSWORD: ${{ inputs.app-password }}
6060+ PDS_URL: ${{ inputs.pds-url }}
6161+ run: |
6262+ FLAGS=""
6363+ if [ "${{ inputs.force }}" = "true" ]; then
6464+ FLAGS="--force"
6565+ fi
6666+ sequoia publish $FLAGS
6767+6868+ - name: Commit back changes
6969+ if: inputs.commit-back == 'true'
7070+ shell: bash
7171+ working-directory: ${{ inputs.working-directory }}
7272+ run: |
7373+ git config user.name "$(git log -1 --format='%an')"
7474+ git config user.email "$(git log -1 --format='%ae')"
7575+ git add -A -- '**/*.md' || true
7676+ if git diff --cached --quiet; then
7777+ echo "No changes to commit"
7878+ else
7979+ git commit -m "chore: update sequoia state [skip ci]"
8080+ git push
8181+ fi
···11# CLI Reference
2233+## `login`
44+55+```bash [Terminal]
66+sequoia login
77+> Login with OAuth (browser-based authentication)
88+99+OPTIONS:
1010+ --logout <str> - Remove OAuth session for a specific DID [optional]
1111+1212+FLAGS:
1313+ --list - List all stored OAuth sessions [optional]
1414+ --help, -h - show help [optional]
1515+```
1616+1717+OAuth is the recommended authentication method as it scopes permissions and refreshes tokens automatically.
1818+319## `auth`
420521```bash [Terminal]
622sequoia auth
77-> Authenticate with your ATProto PDS
2323+> Authenticate with your ATProto PDS using an app password
824925OPTIONS:
1026 --logout <str> - Remove credentials for a specific identity (or all if only one exists) [optional]
···1329 --list - List all stored identities [optional]
1430 --help, -h - show help [optional]
1531```
3232+3333+Use this as an alternative to `login` when OAuth isn't available or for CI environments.
16341735## `init`
1836···6179 --dry-run, -n - Preview what would be synced without making changes [optional]
6280 --help, -h - show help [optional]
6381```
8282+8383+## `update`
8484+8585+```bash [Terminal]
8686+sequoia update
8787+> Update local config or ATProto publication record
8888+8989+FLAGS:
9090+ --help, -h - show help [optional]
9191+```
9292+9393+Interactive command to modify your existing configuration. Choose between:
9494+9595+- **Local configuration**: Edit `sequoia.json` settings including site URL, directory paths, frontmatter mappings, advanced options, and Bluesky settings
9696+- **ATProto publication**: Update your publication record's name, description, URL, icon, and discover visibility
+13
docs/docs/pages/config.mdx
···1717| `frontmatter.slugField` | `string` | No | - | Frontmatter field to use for slug (defaults to filepath) |
1818| `ignore` | `string[]` | No | - | Glob patterns for files to ignore |
1919| `removeIndexFromSlug` | `boolean` | No | `false` | Remove `/index` or `/_index` suffix from slugs |
2020+| `stripDatePrefix` | `boolean` | No | `false` | Remove `YYYY-MM-DD-` date prefixes from slugs (Jekyll-style) |
2021| `bluesky` | `object` | No | - | Bluesky posting configuration |
2122| `bluesky.enabled` | `boolean` | No | `false` | Post to Bluesky when publishing documents |
2223| `bluesky.maxAgeDays` | `number` | No | `30` | Only post documents published within this many days |
···9596```
96979798If the frontmatter field is not found, it falls back to the filepath.
9999+100100+### Jekyll-Style Date Prefixes
101101+102102+Jekyll uses date prefixes in filenames (e.g., `2024-01-15-my-post.md`) for ordering posts. To strip these from generated slugs:
103103+104104+```json
105105+{
106106+ "stripDatePrefix": true
107107+}
108108+```
109109+110110+This transforms `2024-01-15-my-post.md` into the slug `my-post`.
9811199112### Ignoring Files
100113
+9-7
docs/docs/pages/quickstart.mdx
···3131sequoia
3232```
33333434-### Authorize
3535-3636-In order for Sequoia to publish or update records on your PDS, you need to authorize it with your ATProto handle and an app password.
3434+### Login
37353838-:::tip
3939-You can create an app password [here](https://bsky.app/settings/app-passwords)
4040-:::
3636+In order for Sequoia to publish or update records on your PDS, you need to authenticate with your ATProto account.
41374238```bash [Terminal]
4343-sequoia auth
3939+sequoia login
4440```
4141+4242+This will open your browser to complete OAuth authentication, and your sessions will refresh automatically as you use the CLI.
4343+4444+:::tip
4545+Alternatively, you can use `sequoia auth` to authenticate with an [app password](https://bsky.app/settings/app-passwords) instead of OAuth.
4646+:::
45474648### Initialize
4749
docs/docs/public/icon-dark.png
This is a binary file and will not be displayed.
docs/docs/public/og.png
This is a binary file and will not be displayed.
+2-1
package.json
···1111 "build:docs": "cd docs && bun run build",
1212 "build:cli": "cd packages/cli && bun run build",
1313 "deploy:docs": "cd docs && bun run deploy",
1414- "deploy:cli": "cd packages/cli && bun run deploy"
1414+ "deploy:cli": "cd packages/cli && bun run deploy",
1515+ "test:cli": "cd packages/cli && bun test"
1516 },
1617 "devDependencies": {
1718 "@types/bun": "latest",