Skip to content

Commit db37442

Browse files
author
Yann VR
committed
Add scheduling, ecosystem announce, and blog promotion commands
New commands: - schedule: Schedule posts for future publishing - schedule-list: List scheduled posts - schedule-run: Process due scheduled posts (for cron) - schedule-cancel: Cancel a scheduled post - announce-ecosystem: Post from pending announcements file - blog-promote: Generate social posts from blog content Features: - Full scheduling with Prisma storage - Dry-run support for all new commands - Platform validation and rate limiting - Blog content parsing and social post generation Also includes AGENTS.md for AI context
1 parent f84efe2 commit db37442

7 files changed

Lines changed: 1257 additions & 58 deletions

File tree

AGENTS.md

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

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
"axios": "^1.13.2",
5151
"commander": "^11.1.0",
5252
"discord.js": "^14.25.1",
53-
"hyper-post": "link:../../../Library/pnpm/global/5/node_modules/hyper-post",
5453
"mastodon-api": "^1.3.0",
5554
"remark": "^15.0.1",
5655
"remark-stringify": "^11.0.0",

schema.prisma

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,23 @@ model PostAnalytics {
6767
@@map("post_analytics")
6868
}
6969

70+
model ScheduledPost {
71+
id String @id @default(cuid())
72+
contentHash String
73+
title String?
74+
content String
75+
url String?
76+
tags String? // JSON array of tags
77+
platforms String // JSON array of platform names
78+
scheduledAt DateTime
79+
status String @default("pending") // pending, posted, failed, cancelled
80+
createdAt DateTime @default(now())
81+
postedAt DateTime?
82+
error String?
83+
84+
@@index([status, scheduledAt])
85+
@@map("scheduled_posts")
86+
}
87+
7088
// Indexes for performance
7189
// Note: Indexes are automatically created for @unique and foreign key fields

0 commit comments

Comments
 (0)