social-autoposter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # social-autoposter
2
+
3
+ Automated social posting pipeline for Reddit, X/Twitter, LinkedIn, and Moltbook. Install as an AI agent skill or use the standalone Python scripts.
4
+
5
+ [**Browse the data in Datasette Lite**](https://lite.datasette.io/?url=https://raw.githubusercontent.com/m13v/social-autoposter/main/social_posts.db)
6
+
7
+ ## Install as a skill
8
+
9
+ ```bash
10
+ npx social-autoposter init
11
+ ```
12
+
13
+ Then tell your agent: **"set up social autoposter"** — the setup skill walks you through config, DB creation, browser logins, and a test run.
14
+
15
+ To update scripts without touching your config or database:
16
+ ```bash
17
+ npx social-autoposter update
18
+ ```
19
+
20
+ Or set up manually:
21
+ ```bash
22
+ cp config.example.json config.json # edit with your accounts
23
+ sqlite3 social_posts.db < schema.sql # create the database
24
+ bash setup.sh # symlinks + launchd (macOS)
25
+ ```
26
+
27
+ ## How it works
28
+
29
+ ```
30
+ SKILL.md (the playbook)
31
+
32
+ ├── /social-autoposter → find thread, draft, post, log
33
+ ├── /social-autoposter stats → update engagement via API
34
+ ├── /social-autoposter engage → scan replies, respond
35
+ └── /social-autoposter audit → browser-based full audit
36
+
37
+ ├── scripts/find_threads.py → thread discovery (no browser)
38
+ ├── scripts/scan_replies.py → reply scanning (no browser)
39
+ └── scripts/update_stats.py → stats fetching (no browser)
40
+
41
+ ├── skill/run.sh → launchd wrapper (hourly)
42
+ ├── skill/stats.sh → launchd wrapper (6-hourly)
43
+ └── skill/engage.sh → launchd wrapper (2-hourly)
44
+ ```
45
+
46
+ ## Structure
47
+
48
+ ```
49
+ social-autoposter/
50
+ ├── SKILL.md <- skill playbook (generic, publishable)
51
+ ├── config.example.json <- config template (accounts, subreddits, content angle)
52
+ ├── schema.sql <- DB schema
53
+ ├── setup.sh <- creates symlinks, loads launchd agents
54
+ ├── setup/
55
+ │ └── SKILL.md <- interactive setup wizard skill
56
+ ├── scripts/
57
+ │ ├── find_threads.py <- find candidate threads via Reddit/Moltbook API
58
+ │ ├── scan_replies.py <- scan for new replies to our posts via API
59
+ │ └── update_stats.py <- fetch engagement stats via API
60
+ ├── skill/
61
+ │ ├── SKILL.md <- personal skill (hardcoded accounts)
62
+ │ ├── run.sh <- hourly posting (launchd wrapper)
63
+ │ ├── stats.sh <- 6-hourly stats (launchd wrapper)
64
+ │ ├── engage.sh <- 2-hourly engagement (launchd wrapper)
65
+ │ └── logs/ <- runtime logs (gitignored)
66
+ ├── social_posts.db <- SQLite database (committed for Datasette)
67
+ ├── syncfield.sh <- sync SQLite -> Neon Postgres
68
+ └── launchd/ <- macOS LaunchAgent plists
69
+ ```
70
+
71
+ ## For other AI agents
72
+
73
+ The skill is designed to work with any agent that has:
74
+ - **Shell access** (to run Python scripts and sqlite3)
75
+ - **Browser automation** (Playwright, Selenium, etc. for posting)
76
+ - **An LLM** (for drafting comments in the right tone)
77
+
78
+ The Python scripts handle thread discovery, reply scanning, and stats updates without needing a browser or LLM. The SKILL.md is the playbook — any agent reads it and executes the workflows with its own tools.
79
+
80
+ ## Accounts
81
+
82
+ - **Reddit**: u/Deep_Ad1959
83
+ - **X/Twitter**: @m13v_
84
+ - **LinkedIn**: Matthew Diakonov
85
+ - **Moltbook**: matthew-autoposter
package/SKILL.md ADDED
@@ -0,0 +1,317 @@
1
+ ---
2
+ name: social-autoposter
3
+ description: "Automate social media posting across Reddit, X/Twitter, LinkedIn, and Moltbook. Find threads, post comments, track engagement stats, and handle reply engagement. Use when: 'post to social', 'social autoposter', 'find threads', 'audit social posts', 'update post stats', 'engage replies'. Run /social-autoposter to start."
4
+ user_invocable: true
5
+ ---
6
+
7
+ # Social Autoposter
8
+
9
+ Automates finding, posting, and tracking social media comments. Works with any agent that has browser automation and shell access.
10
+
11
+ ## Quick Start
12
+
13
+ | Command | What it does |
14
+ |---------|-------------|
15
+ | `/social-autoposter` | Full posting run (find threads + post + log) |
16
+ | `/social-autoposter stats` | Update engagement stats via API |
17
+ | `/social-autoposter engage` | Scan and reply to responses on our posts |
18
+ | `/social-autoposter audit` | Full browser audit of all posts |
19
+
20
+ ## Config
21
+
22
+ All personal settings live in `config.json` (copy from `config.example.json`). The skill reads this at runtime.
23
+
24
+ Key fields:
25
+ - `accounts` — platform usernames/handles
26
+ - `subreddits` — target subreddits to monitor
27
+ - `content_angle` — your unique perspective for authentic comments
28
+ - `projects` — your products/repos to mention when relevant (with topic keywords)
29
+ - `database` — path to SQLite DB
30
+
31
+ ## Helper Scripts
32
+
33
+ Standalone Python scripts any agent can call directly. No LLM needed for these.
34
+
35
+ | Script | What it does |
36
+ |--------|-------------|
37
+ | `scripts/find_threads.py` | Find candidate threads via Reddit JSON + Moltbook API |
38
+ | `scripts/scan_replies.py` | Scan for new replies to our posts via API |
39
+ | `scripts/update_stats.py` | Fetch engagement stats via API, update DB |
40
+
41
+ Examples:
42
+ ```bash
43
+ python3 scripts/find_threads.py --topic "macOS automation" --limit 5
44
+ python3 scripts/scan_replies.py
45
+ python3 scripts/update_stats.py --quiet
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Critical: No Parallel Posting
51
+
52
+ **NEVER launch multiple agents or parallel tasks for posting.** All posting operations (comments, replies, thread creation) MUST be done sequentially in a single agent. Reasons:
53
+ - There is only one shared browser — parallel agents fight over it and cause timeouts
54
+ - Parallel agents cause duplicate posts (same comment posted twice on the same thread)
55
+ - Browser lock conflicts lead to unpredictable failures
56
+
57
+ This applies to ALL posting workflows: comments on existing threads, self-replies with links, new thread creation, and reply engagement. Even if you have 5 threads to post on, do them one at a time in the same agent.
58
+
59
+ **After each post, always verify** by reloading the page and confirming the comment appears exactly once before moving to the next post.
60
+
61
+ ---
62
+
63
+ ## Workflow: Post (`/social-autoposter`)
64
+
65
+ Find a thread, draft a comment, post it, log it.
66
+
67
+ ### 1. Rate limit check
68
+
69
+ ```sql
70
+ SELECT COUNT(*) FROM posts WHERE posted_at >= datetime('now', '-24 hours')
71
+ ```
72
+ If 40+ posts in the last 24 hours, stop. Max 40/day.
73
+
74
+ ### 2. Find candidate threads
75
+
76
+ **Option A — Use the helper script (preferred, no browser needed):**
77
+ ```bash
78
+ python3 scripts/find_threads.py --include-moltbook
79
+ ```
80
+ This returns a JSON list of candidate threads with dedup already applied.
81
+
82
+ **Option B — Browse manually with browser automation:**
83
+ Browse `/new` and `/hot` across target subreddits (from `config.json`). Also check Moltbook via API.
84
+
85
+ ### 3. Pick the best thread
86
+
87
+ Requirements:
88
+ - You have a genuine angle from your `content_angle` in `config.json`
89
+ - The thread hasn't been posted in before (check `thread_url` in DB)
90
+ - Your last 5 comments don't repeat the same talking points:
91
+ ```sql
92
+ SELECT our_content FROM posts ORDER BY id DESC LIMIT 5
93
+ ```
94
+ - If no thread fits naturally, **stop**. Better to skip than force a bad comment.
95
+
96
+ ### 4. Read the thread
97
+
98
+ Before commenting, read the full thread:
99
+ - Check tone (casual/technical/professional)
100
+ - Read top comments for length and style cues
101
+ - Note thread age (skip stale threads)
102
+ - Identify the best comment to reply to (high-upvote comments get more visibility)
103
+
104
+ ### 5. Draft the comment
105
+
106
+ Follow the **Content Rules** section below. Key points:
107
+ - 2-3 sentences max, match thread energy
108
+ - First person, specific details from your experience
109
+ - No product links in top-level comments (use Tiered Reply Strategy for that)
110
+ - If it sounds like a blog post, rewrite it
111
+
112
+ ### 6. Post it
113
+
114
+ **Reddit** (browser automation):
115
+ - Navigate to `old.reddit.com` thread URL
116
+ - Find the reply box (textarea with class `usertext-edit`)
117
+ - Type the comment, click submit
118
+ - Wait 2-3 seconds, verify the comment appeared
119
+ - Capture the permalink of the new comment
120
+ - Close the tab
121
+
122
+ **X/Twitter** (browser automation):
123
+ - Navigate to the tweet
124
+ - Type reply in the reply box, click Reply
125
+ - Verify the reply posted
126
+ - Capture the URL
127
+
128
+ **LinkedIn** (browser automation):
129
+ - Navigate to the post
130
+ - Type comment, click Post
131
+ - No stable URL available, note as posted
132
+
133
+ **Moltbook** (API — no browser needed):
134
+ ```bash
135
+ source ~/social-autoposter/.env
136
+ curl -s -X POST -H "Authorization: Bearer $MOLTBOOK_API_KEY" \
137
+ -H "Content-Type: application/json" \
138
+ -d '{"title": "...", "content": "...", "type": "text", "submolt_name": "general"}' \
139
+ "https://www.moltbook.com/api/v1/posts"
140
+ ```
141
+ Verify: fetch post by UUID, check `verification_status` is `"verified"`.
142
+ On Moltbook, write as an agent: "my human" not "I".
143
+ Rate limit: max 1 post per 30 minutes.
144
+
145
+ ### 7. Log to database
146
+
147
+ ```sql
148
+ INSERT INTO posts (platform, thread_url, thread_author, thread_author_handle,
149
+ thread_title, thread_content, our_url, our_content, our_account,
150
+ source_summary, status, posted_at)
151
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', datetime('now'));
152
+ ```
153
+
154
+ ### 8. Sync (if configured)
155
+
156
+ If `sync_script` is set in `config.json`, run it to push data to a remote database.
157
+
158
+ ---
159
+
160
+ ## Workflow: Stats (`/social-autoposter stats`)
161
+
162
+ Update engagement metrics for all posts. No browser needed.
163
+
164
+ **Preferred: use the helper script:**
165
+ ```bash
166
+ python3 scripts/update_stats.py
167
+ python3 scripts/update_stats.py --quiet # summary only
168
+ python3 scripts/update_stats.py --json # machine-readable output
169
+ ```
170
+
171
+ This fetches:
172
+ - **Reddit**: comment scores and thread stats via public JSON API. Detects deleted/removed.
173
+ - **Moltbook**: upvotes and comment counts via REST API. Detects deleted posts.
174
+
175
+ Updates `upvotes`, `comments_count`, `thread_engagement`, `engagement_updated_at` in the DB.
176
+
177
+ For **X/Twitter** stats (requires browser): use `/social-autoposter audit` instead.
178
+
179
+ ---
180
+
181
+ ## Workflow: Engage (`/social-autoposter engage`)
182
+
183
+ Discover replies to our posts and respond to them.
184
+
185
+ ### Phase A: Scan for replies (no browser needed)
186
+
187
+ ```bash
188
+ python3 scripts/scan_replies.py
189
+ ```
190
+
191
+ This scans Reddit JSON API + Moltbook API for new replies. Inserts into `replies` table as `pending` or `skipped`.
192
+
193
+ Skip reasons: `too_short` (<5 words), `filtered_author` (AutoModerator, [deleted], self), `too_old` (>7 days), `deleted`.
194
+
195
+ ### Phase B: Respond to pending replies (needs browser/API)
196
+
197
+ Query pending replies:
198
+ ```sql
199
+ SELECT r.id, r.platform, r.their_author, r.their_content, r.their_comment_url,
200
+ r.depth, p.thread_title, p.our_content
201
+ FROM replies r
202
+ JOIN posts p ON r.post_id = p.id
203
+ WHERE r.status='pending'
204
+ ORDER BY r.discovered_at ASC LIMIT 10
205
+ ```
206
+
207
+ For each pending reply:
208
+ 1. Draft a response (2-4 sentences, casual, expand the topic, ask follow-ups)
209
+ 2. Apply the **Tiered Reply Strategy** for project mentions
210
+ 3. Post via browser (Reddit/X) or API (Moltbook)
211
+ 4. Update the reply record:
212
+ ```sql
213
+ UPDATE replies SET status='replied', our_reply_content=?, our_reply_url=?,
214
+ replied_at=datetime('now') WHERE id=?
215
+ ```
216
+
217
+ Max 5 replies per run.
218
+
219
+ ### Phase C: X/Twitter replies (browser required)
220
+
221
+ X has no public API for notifications. To discover X replies:
222
+ 1. Navigate to `https://x.com/notifications/mentions`
223
+ 2. Extract mentions replying to your account
224
+ 3. Filter out already-tracked reply IDs, light acknowledgments, and your own replies
225
+ 4. Respond to substantive replies (max 5)
226
+ 5. Log everything to `replies` table
227
+
228
+ ---
229
+
230
+ ## Workflow: Audit (`/social-autoposter audit`)
231
+
232
+ Full browser-based audit of all posts. Use for X/Twitter stats or visual verification.
233
+
234
+ 1. Query all posts with URLs:
235
+ ```sql
236
+ SELECT id, platform, our_url, status, upvotes, views, comments_count
237
+ FROM posts WHERE our_url IS NOT NULL ORDER BY posted_at DESC
238
+ ```
239
+
240
+ 2. Visit each URL via browser automation. Check:
241
+ - `active`: visible and accessible
242
+ - `deleted`: 404 or "this tweet has been deleted"
243
+ - `removed`: marked as removed by moderator
244
+ - `inactive`: thread locked or archived
245
+
246
+ 3. Capture engagement metrics (upvotes, views, comments) and update DB.
247
+
248
+ 4. Report summary: total checked, by status, top performers.
249
+
250
+ ---
251
+
252
+ ## Content Rules
253
+
254
+ 1. **Write like you're texting a coworker.** Lowercase fine. Sentence fragments fine. Never start with "Makes sense" or "The nuance here is." If it sounds like a blog post, rewrite it.
255
+
256
+ 2. **First person, specific.** Use concrete numbers and real experiences, not generalizations. Say "I" not "you" or "one."
257
+
258
+ 3. **Reply to top comments, not just OP.** High-upvote comments get more eyeballs.
259
+
260
+ 4. **Only comment when you have a real angle from your work.** Use `content_angle` from `config.json`. If the thread doesn't connect to your experience, skip it.
261
+
262
+ 5. **No product links in top-level comments.** Earn attention first. Links come later via the Tiered Reply Strategy.
263
+
264
+ 6. **On Moltbook, write as an agent.** Use "my human" not "I". First-person agent perspective.
265
+
266
+ 7. **Log everything.** Every thread and comment goes in the database.
267
+
268
+ ### Bad vs Good
269
+
270
+ BAD: "Makes sense — Claude already tries to `| tail -n 50` on its own but by then the tokens are already in context."
271
+ GOOD: "gonna try this — I run 5 agents in parallel and my API bill is becoming a second rent payment"
272
+
273
+ BAD: "What everyone here is describing is basically specification-driven development."
274
+ GOOD: "I spend more time writing CLAUDE.md specs than I ever spent writing code. the irony is I'm basically doing waterfall now and shipping faster than ever."
275
+
276
+ ---
277
+
278
+ ## Tiered Reply Strategy
279
+
280
+ When replying to comments on our posts, escalate project mentions based on conversation context.
281
+
282
+ **Tier 1 — Default (no link):** Genuine engagement. Expand the topic, ask follow-ups. Most replies.
283
+
284
+ **Tier 2 — Natural mention:** Conversation touches something you're building. Mention the project casually. Include link only if it adds value. Triggers:
285
+ - "what are you working on" / "what tool do you use"
286
+ - They describe a problem matching a project's `topics` from `config.json`
287
+ - Conversation is 2+ replies deep (genuine interest)
288
+
289
+ **Tier 3 — Direct ask:** They explicitly ask for a link, to try it, if it's open source. Give it immediately.
290
+
291
+ ---
292
+
293
+ ## Database Schema
294
+
295
+ ```sql
296
+ -- Core tables (see schema.sql for full DDL)
297
+ posts -- everything we post (platform, urls, content, engagement, status)
298
+ threads -- threads we've discovered
299
+ our_posts -- backward-compat post tracking
300
+ replies -- replies to our posts and our responses
301
+ ```
302
+
303
+ Key fields in `posts`: `id, platform, thread_url, thread_title, our_url, our_content, our_account, posted_at, status, upvotes, comments_count, views, source_summary`
304
+
305
+ Key fields in `replies`: `id, post_id, platform, their_author, their_content, our_reply_content, status (pending|replied|skipped|error), depth`
306
+
307
+ ---
308
+
309
+ ## Platform Reference
310
+
311
+ **Reddit:** Use `old.reddit.com` for reliable automation. Comment box: textarea with class `usertext-edit`. No posting API — browser only.
312
+
313
+ **X/Twitter:** Reply to existing tweets. 1-2 sentences ideal. No public API for notifications — browser only.
314
+
315
+ **LinkedIn:** Professional tone, brief. Comments don't have stable URLs. Browser only.
316
+
317
+ **Moltbook:** Full REST API, no browser needed. Base: `https://www.moltbook.com/api/v1`. Auth: `Bearer $MOLTBOOK_API_KEY`. Agent-first platform — write as an agent.
package/bin/cli.js ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+ const os = require('os');
7
+ const { spawnSync } = require('child_process');
8
+
9
+ const DEST = path.join(os.homedir(), 'social-autoposter');
10
+ const PKG_ROOT = path.join(__dirname, '..');
11
+
12
+ // Files/dirs to copy from npm package to ~/social-autoposter
13
+ const COPY_TARGETS = [
14
+ 'scripts',
15
+ 'schema.sql',
16
+ 'config.example.json',
17
+ 'SKILL.md',
18
+ 'skill',
19
+ 'setup',
20
+ 'launchd',
21
+ 'syncfield.sh',
22
+ ];
23
+
24
+ // Never overwrite these user files during update
25
+ const USER_FILES = new Set(['config.json', 'social_posts.db', '.env']);
26
+
27
+ function copyDir(src, dest) {
28
+ fs.mkdirSync(dest, { recursive: true });
29
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
30
+ const srcPath = path.join(src, entry.name);
31
+ const destPath = path.join(dest, entry.name);
32
+ if (entry.isDirectory()) {
33
+ copyDir(srcPath, destPath);
34
+ } else {
35
+ fs.copyFileSync(srcPath, destPath);
36
+ }
37
+ }
38
+ }
39
+
40
+ function linkOrRelink(target, linkPath) {
41
+ try { fs.rmSync(linkPath, { recursive: true, force: true }); } catch {}
42
+ fs.symlinkSync(target, linkPath);
43
+ }
44
+
45
+ function init() {
46
+ console.log('Setting up social-autoposter in', DEST);
47
+ fs.mkdirSync(DEST, { recursive: true });
48
+
49
+ // Copy all package files
50
+ for (const f of COPY_TARGETS) {
51
+ const src = path.join(PKG_ROOT, f);
52
+ const dest = path.join(DEST, f);
53
+ if (!fs.existsSync(src)) continue;
54
+ const stat = fs.statSync(src);
55
+ if (stat.isDirectory()) {
56
+ copyDir(src, dest);
57
+ } else {
58
+ fs.copyFileSync(src, dest);
59
+ }
60
+ console.log(' copied', f);
61
+ }
62
+
63
+ // config.json — only if it doesn't exist
64
+ const configDest = path.join(DEST, 'config.json');
65
+ if (!fs.existsSync(configDest)) {
66
+ fs.copyFileSync(path.join(PKG_ROOT, 'config.example.json'), configDest);
67
+ console.log(' created config.json from template');
68
+ } else {
69
+ console.log(' config.json exists — skipping');
70
+ }
71
+
72
+ // Create DB from schema if missing
73
+ const dbPath = path.join(DEST, 'social_posts.db');
74
+ if (!fs.existsSync(dbPath)) {
75
+ const schemaPath = path.join(DEST, 'schema.sql');
76
+ const result = spawnSync('sqlite3', [dbPath], {
77
+ input: fs.readFileSync(schemaPath),
78
+ stdio: ['pipe', 'inherit', 'inherit'],
79
+ });
80
+ if (result.status === 0) {
81
+ console.log(' created social_posts.db');
82
+ } else {
83
+ console.warn(' WARNING: sqlite3 failed — create DB manually:');
84
+ console.warn(' sqlite3 ~/social-autoposter/social_posts.db < ~/social-autoposter/schema.sql');
85
+ }
86
+ } else {
87
+ console.log(' social_posts.db exists — skipping');
88
+ }
89
+
90
+ // Skill symlink: ~/.claude/skills/social-autoposter -> ~/social-autoposter/skill
91
+ const skillsDir = path.join(os.homedir(), '.claude', 'skills');
92
+ fs.mkdirSync(skillsDir, { recursive: true });
93
+ linkOrRelink(path.join(DEST, 'skill'), path.join(skillsDir, 'social-autoposter'));
94
+ console.log(' ~/.claude/skills/social-autoposter ->', path.join(DEST, 'skill'));
95
+
96
+ // DB symlink: ~/.claude/social_posts.db -> ~/social-autoposter/social_posts.db
97
+ const claudeDir = path.join(os.homedir(), '.claude');
98
+ try {
99
+ linkOrRelink(dbPath, path.join(claudeDir, 'social_posts.db'));
100
+ console.log(' ~/.claude/social_posts.db ->', dbPath);
101
+ } catch {}
102
+
103
+ console.log('');
104
+ console.log('Done! Next steps:');
105
+ console.log(' 1. Edit ~/social-autoposter/config.json with your accounts');
106
+ console.log(' 2. Tell your Claude agent: "set up social autoposter"');
107
+ console.log(' (uses the setup/SKILL.md wizard for browser login verification)');
108
+ console.log(' 3. Or configure manually and run: bash ~/social-autoposter/setup.sh');
109
+ }
110
+
111
+ function update() {
112
+ if (!fs.existsSync(DEST)) {
113
+ console.error('Not installed. Run: npx social-autoposter init');
114
+ process.exit(1);
115
+ }
116
+
117
+ console.log('Updating social-autoposter...');
118
+
119
+ for (const f of COPY_TARGETS) {
120
+ if (USER_FILES.has(f)) {
121
+ console.log(' skipping', f, '(user file)');
122
+ continue;
123
+ }
124
+ const src = path.join(PKG_ROOT, f);
125
+ const dest = path.join(DEST, f);
126
+ if (!fs.existsSync(src)) continue;
127
+ const stat = fs.statSync(src);
128
+ if (stat.isDirectory()) {
129
+ copyDir(src, dest);
130
+ } else {
131
+ fs.copyFileSync(src, dest);
132
+ }
133
+ console.log(' updated', f);
134
+ }
135
+
136
+ // Re-symlink skill in case it broke
137
+ const skillsDir = path.join(os.homedir(), '.claude', 'skills');
138
+ try {
139
+ linkOrRelink(path.join(DEST, 'skill'), path.join(skillsDir, 'social-autoposter'));
140
+ console.log(' re-linked ~/.claude/skills/social-autoposter');
141
+ } catch {}
142
+
143
+ console.log('');
144
+ console.log('Update complete. config.json and social_posts.db were preserved.');
145
+ }
146
+
147
+ const cmd = process.argv[2];
148
+ if (cmd === 'init') {
149
+ init();
150
+ } else if (cmd === 'update') {
151
+ update();
152
+ } else {
153
+ console.log('social-autoposter — automated social posting for Claude agents');
154
+ console.log('');
155
+ console.log('Usage:');
156
+ console.log(' npx social-autoposter init first-time setup');
157
+ console.log(' npx social-autoposter update update scripts, preserve config');
158
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "database": "~/social-autoposter/social_posts.db",
3
+ "prompt_db": "~/claude-prompt-db/prompts.db",
4
+ "sync_script": "~/social-autoposter/syncfield.sh",
5
+ "stats_script": "~/.claude/skills/social-autoposter/stats.sh",
6
+
7
+ "accounts": {
8
+ "reddit": {
9
+ "username": "your-reddit-username",
10
+ "login_method": "browser"
11
+ },
12
+ "twitter": {
13
+ "handle": "@your-twitter-handle",
14
+ "login_method": "browser"
15
+ },
16
+ "linkedin": {
17
+ "name": "Your Name",
18
+ "login_method": "browser"
19
+ },
20
+ "moltbook": {
21
+ "username": "your-moltbook-username",
22
+ "api_key_env": "MOLTBOOK_API_KEY",
23
+ "env_file": "~/social-autoposter/.env"
24
+ }
25
+ },
26
+
27
+ "subreddits": ["ClaudeAI", "programming", "webdev", "devops"],
28
+
29
+ "content_angle": "Describe your unique experience/perspective that gives you authentic angles for comments. Example: 'Building a macOS desktop AI agent. Experience with Swift, browser automation, MCP tools, and running 5 Claude agents in parallel.'",
30
+
31
+ "projects": [
32
+ {
33
+ "name": "My Main Project",
34
+ "description": "One-line description of what it does",
35
+ "website": "https://example.com",
36
+ "github": "",
37
+ "topics": ["desktop automation", "AI agents", "browser control"],
38
+ "_comment": "Topics are keywords that trigger mentioning this project in replies. When a conversation touches these topics, the tiered reply strategy may naturally mention this project."
39
+ },
40
+ {
41
+ "name": "My Open Source Tool",
42
+ "description": "What the tool does",
43
+ "website": "",
44
+ "github": "https://github.com/you/your-repo",
45
+ "topics": ["macOS automation", "accessibility APIs"]
46
+ }
47
+ ],
48
+
49
+ "open_source_links": [],
50
+ "launchd_label": "com.yourname.social-stats"
51
+ }
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.m13v.social-autoposter</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>/bin/bash</string>
10
+ <string>/Users/matthewdi/.claude/skills/social-autoposter/skill/run.sh</string>
11
+ </array>
12
+ <key>StartInterval</key>
13
+ <integer>3600</integer>
14
+ <key>StandardOutPath</key>
15
+ <string>/Users/matthewdi/.claude/skills/social-autoposter/logs/launchd-stdout.log</string>
16
+ <key>StandardErrorPath</key>
17
+ <string>/Users/matthewdi/.claude/skills/social-autoposter/logs/launchd-stderr.log</string>
18
+ <key>EnvironmentVariables</key>
19
+ <dict>
20
+ <key>PATH</key>
21
+ <string>/Users/matthewdi/.nvm/versions/node/v20.19.4/bin:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
22
+ <key>HOME</key>
23
+ <string>/Users/matthewdi</string>
24
+ </dict>
25
+ <key>RunAtLoad</key>
26
+ <true/>
27
+ </dict>
28
+ </plist>
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.m13v.social-engage</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>/bin/bash</string>
10
+ <string>/Users/matthewdi/.claude/skills/social-autoposter/skill/engage.sh</string>
11
+ </array>
12
+ <key>StartInterval</key>
13
+ <integer>21600</integer>
14
+ <key>StandardOutPath</key>
15
+ <string>/Users/matthewdi/.claude/skills/social-autoposter/logs/launchd-engage-stdout.log</string>
16
+ <key>StandardErrorPath</key>
17
+ <string>/Users/matthewdi/.claude/skills/social-autoposter/logs/launchd-engage-stderr.log</string>
18
+ <key>EnvironmentVariables</key>
19
+ <dict>
20
+ <key>PATH</key>
21
+ <string>/Users/matthewdi/.nvm/versions/node/v20.19.4/bin:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
22
+ <key>HOME</key>
23
+ <string>/Users/matthewdi</string>
24
+ </dict>
25
+ <key>RunAtLoad</key>
26
+ <false/>
27
+ </dict>
28
+ </plist>