|
| 1 | +# AGENTS.md - HyperPost |
| 2 | + |
| 3 | +> Guidelines for AI agents working in this codebase. |
| 4 | +> |
| 5 | +> Also read: `~/dev/hyperdrift/AGENTS.md` — workspace-level context (deploy tooling, fleet management, HD infra). |
| 6 | +
|
| 7 | +## Project Overview |
| 8 | + |
| 9 | +**HyperPost** is a unified social media posting CLI tool that publishes content to multiple social networks simultaneously. Built with TypeScript, it uses Prisma for database operations and supports Mastodon, Bluesky, Discord, Reddit, Dev.to, and Medium. |
| 10 | + |
| 11 | +Part of the **HyperDrift** ecosystem - open-source tools for independent developers. |
| 12 | + |
| 13 | +## Essential Commands |
| 14 | + |
| 15 | +```bash |
| 16 | +# Package manager - ALWAYS use pnpm |
| 17 | +pnpm install # Install dependencies |
| 18 | +pnpm build # Build with tsup (outputs to dist/) |
| 19 | +pnpm dev # Watch mode development |
| 20 | +pnpm test # Run Jest tests |
| 21 | +pnpm lint # ESLint on src/**/*.ts |
| 22 | +pnpm typecheck # TypeScript type checking (tsc --noEmit) |
| 23 | + |
| 24 | +# Database (Prisma with SQLite) |
| 25 | +pnpm db:generate # Generate Prisma client after schema changes |
| 26 | +pnpm db:push # Push schema to database |
| 27 | +pnpm db:studio # Open Prisma Studio GUI |
| 28 | +pnpm db:migrate # Create migrations for production |
| 29 | + |
| 30 | +# CLI testing |
| 31 | +pnpm cli # Run CLI directly (node dist/cli.js) |
| 32 | +hyper-post setup # Interactive setup wizard |
| 33 | +hyper-post post -c "..." # Post to all platforms |
| 34 | +hyper-post platforms # List configured platforms |
| 35 | +``` |
| 36 | + |
| 37 | +## Project Structure |
| 38 | + |
| 39 | +``` |
| 40 | +src/ |
| 41 | +├── HyperPost.ts # Main class - orchestrates multi-platform posting |
| 42 | +├── cli.ts # Commander-based CLI implementation |
| 43 | +├── database.ts # Prisma client singleton |
| 44 | +├── index.ts # Public API exports |
| 45 | +├── setup.ts # Interactive setup wizard |
| 46 | +├── signup-manager.ts # Manages credentials in ~/.config/hyper-post/ |
| 47 | +├── signup-templates.ts # Platform signup requirements and templates |
| 48 | +├── platforms/ |
| 49 | +│ ├── BasePlatform.ts # Abstract base class for all platforms |
| 50 | +│ ├── BlueskyPlatform.ts |
| 51 | +│ ├── MastodonPlatform.ts |
| 52 | +│ ├── DiscordPlatform.ts |
| 53 | +│ ├── RedditPlatform.ts |
| 54 | +│ ├── DevtoPlatform.ts |
| 55 | +│ ├── MediumPlatform.ts |
| 56 | +│ └── index.ts # Platform exports |
| 57 | +├── types/ |
| 58 | +│ └── index.ts # TypeScript interfaces (SocialPost, PostingResult, etc.) |
| 59 | +└── utils/ |
| 60 | + └── contentFormat.ts # MDX/Markdown detection and conversion for Medium |
| 61 | +
|
| 62 | +schema.prisma # Database schema (SQLite default) |
| 63 | +tsup.config.ts # Build configuration (CJS + ESM) |
| 64 | +``` |
| 65 | + |
| 66 | +## Architecture Patterns |
| 67 | + |
| 68 | +### Platform Pattern |
| 69 | + |
| 70 | +All platforms extend `BasePlatform` and implement: |
| 71 | + |
| 72 | +```typescript |
| 73 | +abstract class BasePlatform { |
| 74 | + abstract get name(): string; // lowercase identifier |
| 75 | + abstract get displayName(): string; // human-readable name |
| 76 | + abstract post(content: SocialPost): Promise<PostingResult>; |
| 77 | + abstract gatherAnalytics(postUrl: string): Promise<PostAnalytics>; |
| 78 | + abstract discoverPosts(limit?: number): Promise<...>; |
| 79 | + protected abstract getRequiredCredentials(): string[]; |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +### Adding a New Platform |
| 84 | + |
| 85 | +1. Create `src/platforms/NewPlatform.ts` extending `BasePlatform` |
| 86 | +2. Export from `src/platforms/index.ts` |
| 87 | +3. Add to `SupportedPlatforms` type in `src/types/index.ts` |
| 88 | +4. Add initialization in `HyperPost.initializePlatforms()` |
| 89 | +5. Add platform data in `HyperPost.initializeDatabase()` |
| 90 | +6. Add signup requirements in `src/signup-templates.ts` |
| 91 | + |
| 92 | +### Credential Storage |
| 93 | + |
| 94 | +- User credentials stored in `~/.config/hyper-post/signup-data.json` |
| 95 | +- Config defaults in `~/.config/hyper-post/config.json` |
| 96 | +- `SignupManager` class handles all credential operations |
| 97 | +- Environment variables override stored credentials |
| 98 | + |
| 99 | +### Database Schema |
| 100 | + |
| 101 | +```prisma |
| 102 | +Post # Content with SHA-256 hash for deduplication |
| 103 | +Platform # Platform metadata (mastodon, bluesky, etc.) |
| 104 | +PostPlatform # Many-to-many: which posts went to which platforms |
| 105 | +PostAnalytics # Engagement metrics per post-platform |
| 106 | +ScheduledPost # Future posts with status tracking |
| 107 | +``` |
| 108 | + |
| 109 | +## Key Types |
| 110 | + |
| 111 | +```typescript |
| 112 | +interface SocialPost { |
| 113 | + content: string; |
| 114 | + title?: string; |
| 115 | + url?: string; |
| 116 | + imageUrl?: string; |
| 117 | + tags?: string[]; |
| 118 | +} |
| 119 | + |
| 120 | +interface PostingResult { |
| 121 | + platform: string; |
| 122 | + success: boolean; |
| 123 | + postId?: string; |
| 124 | + url?: string; |
| 125 | + error?: string; |
| 126 | +} |
| 127 | + |
| 128 | +type SupportedPlatforms = 'mastodon' | 'bluesky' | 'discord' | 'reddit' | 'devto' | 'medium' | ...; |
| 129 | +``` |
| 130 | + |
| 131 | +## Build System |
| 132 | + |
| 133 | +- **tsup** bundles the project into `dist/` |
| 134 | +- Three entry points: `index.ts` (library), `cli.ts` (CLI), `setup.ts` (wizard) |
| 135 | +- CLI and setup get `#!/usr/bin/env node` banner |
| 136 | +- Library outputs both CJS and ESM |
| 137 | +- TypeScript target: ES2022 |
| 138 | + |
| 139 | +## Coding Conventions |
| 140 | + |
| 141 | +### TypeScript |
| 142 | +- Strict mode enabled |
| 143 | +- Use `async/await` for all async operations |
| 144 | +- Return `PostingResult` objects from platform `post()` methods |
| 145 | +- Use `createResult()` helper in platforms for consistent returns |
| 146 | + |
| 147 | +### Error Handling |
| 148 | +- Platforms catch errors and return failure results (don't throw) |
| 149 | +- Use `console.warn()` for non-fatal issues (analytics failures, etc.) |
| 150 | +- CLI uses `process.exit(1)` for fatal errors |
| 151 | + |
| 152 | +### Naming |
| 153 | +- Platform classes: `{Name}Platform` (e.g., `BlueskyPlatform`) |
| 154 | +- Platform identifiers: lowercase (e.g., `bluesky`) |
| 155 | +- Credential keys: camelCase (e.g., `accessToken`, `integrationToken`) |
| 156 | + |
| 157 | +### Imports |
| 158 | +- Use named imports |
| 159 | +- Platform implementations use `require()` for some packages (e.g., `mastodon-api`) |
| 160 | +- Group imports: external packages, then internal modules |
| 161 | + |
| 162 | +## Testing |
| 163 | + |
| 164 | +- Jest is configured but no test files exist yet |
| 165 | +- Test commands exist in `package.json` |
| 166 | +- Recommended: add tests in `__tests__/` or `*.test.ts` files |
| 167 | + |
| 168 | +## CLI Commands |
| 169 | + |
| 170 | +| Command | Description | |
| 171 | +|---------|-------------| |
| 172 | +| `post` | Post content to platforms (`-c`, `-t`, `-u`, `--tags`, `-p`, `--dry-run`) | |
| 173 | +| `platforms` | List/test configured platforms | |
| 174 | +| `setup` | Interactive setup wizard | |
| 175 | +| `history` | View posting history | |
| 176 | +| `analytics` | View engagement data | |
| 177 | +| `gather-analytics` | Fetch fresh metrics from platforms | |
| 178 | +| `discover-posts` | Find existing posts on platforms | |
| 179 | +| `import-post` | Import external post for tracking | |
| 180 | +| `repost` | Repost content to additional platforms | |
| 181 | +| `schedule` | Schedule future posts | |
| 182 | +| `schedule-list` | List scheduled posts | |
| 183 | +| `schedule-cancel` | Cancel scheduled post | |
| 184 | +| `schedule-run` | Process due posts (for cron) | |
| 185 | +| `promote` | **Blog promotion** - parse MDX posts and share to platforms | |
| 186 | + |
| 187 | +### Blog Promotion (`promote`) |
| 188 | + |
| 189 | +The `promote` command reads MDX blog posts from a content directory and posts them to social platforms: |
| 190 | + |
| 191 | +```bash |
| 192 | +# List available blog posts |
| 193 | +hyper-post promote --list |
| 194 | + |
| 195 | +# Promote a specific article |
| 196 | +hyper-post promote --slug revela-part-1-architecture --dry-run |
| 197 | + |
| 198 | +# Promote with full content to Dev.to/Medium |
| 199 | +hyper-post promote --slug my-article --full-content |
| 200 | + |
| 201 | +# Schedule promotion for later |
| 202 | +hyper-post promote --slug my-article --schedule "2025-02-01 10:00" |
| 203 | + |
| 204 | +# Promote recent posts (last 7 days) |
| 205 | +hyper-post promote --recent 7 |
| 206 | + |
| 207 | +# Custom blog directory |
| 208 | +hyper-post promote --blog-dir /path/to/content/blog --base-url https://mysite.com |
| 209 | +``` |
| 210 | + |
| 211 | +Default blog directory: `/Users/yann/dev/hyperdrift-io/hyper-drift/content/blog` |
| 212 | + |
| 213 | +## Platform-Specific Notes |
| 214 | + |
| 215 | +### Medium |
| 216 | +- Requires Markdown content format |
| 217 | +- Uses `contentFormat.ts` utilities for MDX → Markdown conversion |
| 218 | +- Tags limited to 5, max 25 chars each |
| 219 | +- Hashtags extracted from content automatically |
| 220 | + |
| 221 | +### Bluesky |
| 222 | +- Uses `@atproto/api` SDK |
| 223 | +- Creates rich text with facets for link detection |
| 224 | +- URL embeds created as `app.bsky.embed.external` |
| 225 | + |
| 226 | +### Mastodon |
| 227 | +- Uses `mastodon-api` npm package |
| 228 | +- Instance URL required in credentials |
| 229 | +- Tags appended as hashtags to status text |
| 230 | + |
| 231 | +### Discord |
| 232 | +- Uses `discord.js` |
| 233 | +- Requires bot token and channel ID |
| 234 | +- Posts as bot messages |
| 235 | + |
| 236 | +## Gotchas |
| 237 | + |
| 238 | +1. **Build before CLI testing**: Always run `pnpm build` before testing CLI changes |
| 239 | +2. **Prisma generation**: Run `pnpm db:generate` after any `schema.prisma` changes |
| 240 | +3. **Duplicate detection**: Content is hashed (title + content + url) with 24-hour window |
| 241 | +4. **Platform initialization**: Database platforms are upserted on `HyperPost` construction |
| 242 | +5. **Medium content**: Must be Markdown - MDX auto-converted, plain text rejected |
| 243 | +6. **Credential precedence**: Environment variables override stored credentials |
| 244 | +7. **pnpm only**: Project uses pnpm workspace - don't use npm or yarn |
| 245 | + |
| 246 | +## Dependencies |
| 247 | + |
| 248 | +Key production dependencies: |
| 249 | +- `@atproto/api` - Bluesky API |
| 250 | +- `@prisma/client` - Database ORM |
| 251 | +- `axios` - HTTP client (Medium, Dev.to, Reddit) |
| 252 | +- `commander` - CLI framework |
| 253 | +- `discord.js` - Discord API |
| 254 | +- `mastodon-api` - Mastodon API |
| 255 | +- `@mdx-js/mdx` / `remark` - Content format conversion |
| 256 | +- `zod` - Schema validation |
| 257 | + |
| 258 | +## File Locations |
| 259 | + |
| 260 | +- Config: `~/.config/hyper-post/` |
| 261 | +- Database: `./hyperpost.db` (SQLite, in project root) |
| 262 | +- Built output: `./dist/` |
| 263 | +- Source: `./src/` |
| 264 | + |
| 265 | +## Integration with HyperDrift Blog |
| 266 | + |
| 267 | +HyperPost is designed to promote articles from the HyperDrift blog (`/Users/yann/dev/hyperdrift-io/hyper-drift/content/blog`). |
| 268 | + |
| 269 | +### Workflow for New Blog Posts |
| 270 | + |
| 271 | +1. **Write the article** in MDX format with frontmatter: |
| 272 | + ```yaml |
| 273 | + --- |
| 274 | + title: "Article Title" |
| 275 | + date: "2025-02-01T10:00:00Z" |
| 276 | + excerpt: "Brief summary for social posts" |
| 277 | + tags: ["tag1", "tag2"] |
| 278 | + --- |
| 279 | + ``` |
| 280 | + |
| 281 | +2. **Preview the promotion**: |
| 282 | + ```bash |
| 283 | + hyper-post promote --slug article-slug --dry-run |
| 284 | + ``` |
| 285 | + |
| 286 | +3. **Post to all platforms**: |
| 287 | + ```bash |
| 288 | + hyper-post promote --slug article-slug |
| 289 | + ``` |
| 290 | + |
| 291 | +4. **Or schedule for optimal timing**: |
| 292 | + ```bash |
| 293 | + hyper-post promote --slug article-slug --schedule "2025-02-01 10:00" |
| 294 | + ``` |
| 295 | + |
| 296 | +### Platform Strategy |
| 297 | + |
| 298 | +- **Short-form** (Mastodon, Bluesky): Uses excerpt |
| 299 | +- **Long-form** (Dev.to, Medium): Use `--full-content` for cross-posting the full article |
| 300 | + |
| 301 | +### Currently Configured Platforms |
| 302 | + |
| 303 | +Run `hyper-post platforms` to see active platforms. As of now: |
| 304 | +- Mastodon (mastodon.social/@hyperdrift) |
| 305 | +- Bluesky (hyper-drift.bsky.social) |
| 306 | +- Dev.to (dev.to/hyperdrift) |
| 307 | + |
| 308 | +### Missing Platforms to Consider |
| 309 | + |
| 310 | +- **Reddit** - Requires OAuth setup (client_id, client_secret, username, password) |
| 311 | +- **Medium** - Requires integration token |
| 312 | +- **Discord** - Requires bot token and channel ID |
| 313 | +- **Hashnode** - Developer blogging platform (not yet implemented) |
| 314 | +- **Daily.dev** - Content aggregation via Squads (not yet implemented) |
0 commit comments