social-autoposter 1.6.41 → 1.6.43
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/package.json +1 -1
- package/scripts/twitter_browser.py +35 -0
- package/scripts/twitter_post_plan.py +24 -0
- package/setup/SKILL.md +48 -226
package/package.json
CHANGED
|
@@ -529,6 +529,35 @@ def _wait_for_reply_textbox(page, total_timeout_ms=45000):
|
|
|
529
529
|
return None
|
|
530
530
|
|
|
531
531
|
|
|
532
|
+
# Post-action interstitials X shows AFTER a successful reply (e.g. the
|
|
533
|
+
# "Unlock more on X" graduated-access sheet). They don't block the post that
|
|
534
|
+
# triggered them, but the sheet stays up and overlays the composer on the NEXT
|
|
535
|
+
# reply in a batch -> spurious reply_box_not_found for posts 2..N. We dismiss
|
|
536
|
+
# them deterministically before looking for the reply box. Targeted by the
|
|
537
|
+
# sheet's CTA label so we never touch a real compose/confirm dialog (those have
|
|
538
|
+
# no "Got it"); best-effort, fast, never raises.
|
|
539
|
+
_OVERLAY_DISMISS_LABELS = ("Got it", "Dismiss")
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _dismiss_known_overlays(page) -> bool:
|
|
543
|
+
"""Click-dismiss any known X nudge sheet currently covering the page.
|
|
544
|
+
|
|
545
|
+
Returns True if something was dismissed. Safe to call on every reply: it is
|
|
546
|
+
a no-op when no known overlay is present and swallows all errors."""
|
|
547
|
+
for label in _OVERLAY_DISMISS_LABELS:
|
|
548
|
+
try:
|
|
549
|
+
btn = page.get_by_role("button", name=label, exact=True).first
|
|
550
|
+
if btn.count() > 0 and btn.is_visible():
|
|
551
|
+
btn.click(timeout=2000)
|
|
552
|
+
page.wait_for_timeout(800)
|
|
553
|
+
print(f"[overlay] dismissed known interstitial via '{label}' button",
|
|
554
|
+
file=sys.stderr)
|
|
555
|
+
return True
|
|
556
|
+
except Exception:
|
|
557
|
+
pass
|
|
558
|
+
return False
|
|
559
|
+
|
|
560
|
+
|
|
532
561
|
def _dump_reply_failure_diag(page, tweet_url):
|
|
533
562
|
"""Dump screenshot + DOM state on reply_box_not_found. Returns a diag dict."""
|
|
534
563
|
import time as _t
|
|
@@ -812,6 +841,12 @@ def reply_to_tweet(tweet_url, text, apply_campaigns=True):
|
|
|
812
841
|
tweet_not_found = True
|
|
813
842
|
break
|
|
814
843
|
|
|
844
|
+
# A nudge sheet left over from the previous reply in this batch
|
|
845
|
+
# (e.g. "Unlock more on X") can sit on top of the composer and
|
|
846
|
+
# mask tweetTextarea_0. Clear it first so the wait below sees the
|
|
847
|
+
# real reply box instead of failing reply_box_not_found.
|
|
848
|
+
_dismiss_known_overlays(page)
|
|
849
|
+
|
|
815
850
|
reply_box = _wait_for_reply_textbox(page, total_timeout_ms=45000)
|
|
816
851
|
if reply_box:
|
|
817
852
|
break
|
|
@@ -858,6 +858,11 @@ def main() -> int:
|
|
|
858
858
|
ap = argparse.ArgumentParser()
|
|
859
859
|
ap.add_argument("--plan", required=True,
|
|
860
860
|
help="Path to the plan JSON file (read-only here)")
|
|
861
|
+
ap.add_argument("--post-unapproved", action="store_true",
|
|
862
|
+
help="Post candidates even when the plan marks them "
|
|
863
|
+
"approved=false. The MCP review path already filters to "
|
|
864
|
+
"approved-only, and autopilot/legacy plans omit the key; "
|
|
865
|
+
"this is the explicit override for an intentional direct run.")
|
|
861
866
|
args = ap.parse_args()
|
|
862
867
|
|
|
863
868
|
plan_path = Path(args.plan)
|
|
@@ -909,6 +914,25 @@ def main() -> int:
|
|
|
909
914
|
fail_reasons: dict[str, int] = {}
|
|
910
915
|
skip_reasons: dict[str, int] = {}
|
|
911
916
|
|
|
917
|
+
# Approval gate. A plan that went through the MCP review carries an
|
|
918
|
+
# `approved` flag per candidate (set in mcp/dist/index.js). Honor it here so
|
|
919
|
+
# a DIRECT `--plan` run — bypassing the elicitation form — can't publish
|
|
920
|
+
# drafts the user never ticked. Plans that never had review (autopilot,
|
|
921
|
+
# legacy) omit the key entirely and pass through untouched. Override with
|
|
922
|
+
# --post-unapproved.
|
|
923
|
+
if not args.post_unapproved:
|
|
924
|
+
_kept = []
|
|
925
|
+
for c in candidates:
|
|
926
|
+
if "approved" in c and not c.get("approved"):
|
|
927
|
+
skipped += 1
|
|
928
|
+
skip_reasons["not_approved"] = skip_reasons.get("not_approved", 0) + 1
|
|
929
|
+
else:
|
|
930
|
+
_kept.append(c)
|
|
931
|
+
if skip_reasons.get("not_approved"):
|
|
932
|
+
print(f"[post] {skip_reasons['not_approved']} candidate(s) skipped: not "
|
|
933
|
+
f"approved in plan (pass --post-unapproved to override)", flush=True)
|
|
934
|
+
candidates = _kept
|
|
935
|
+
|
|
912
936
|
for c in candidates:
|
|
913
937
|
try:
|
|
914
938
|
outcome, reason = post_one(c, picker_assignment=picker_assignment)
|
package/setup/SKILL.md
CHANGED
|
@@ -1,271 +1,93 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: social-autoposter-setup
|
|
3
|
-
description: "Set up social-autoposter for a new user.
|
|
3
|
+
description: "Set up social-autoposter for a new user. Configures the product (website, ICP, voice, search topics), seeds search topics into the backend, connects X/Twitter (auto-detecting the real handle), and verifies with a draft cycle. Use when: 'set up social autoposter', 'install social autoposter', 'configure social posting'."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Social Autoposter Setup
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Set up social-autoposter for a new user. Walk them through it conversationally — don't dump a form.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Architecture (read this first)
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
12
|
+
- **Config**: `~/social-autoposter/config.json` — `projects[]` (what to post about) and `accounts` (where to post).
|
|
13
|
+
- **Data + stats**: a backend HTTP API (`https://s4l.ai`), scoped by a **stable per-install identity** auto-created in `identity.json`. There is **NO local Postgres and no `DATABASE_URL`** to configure — that was the old architecture; ignore any reference to psycopg2 / `SELECT ... FROM posts`.
|
|
14
|
+
- **Search topics**: the X cycle's search queries live in the DB table `project_search_topics`, **seeded from each project's `search_topics`** at setup. A project with no seeded topics has nothing to scan and the draft cycle returns empty — so topics are required.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Which path to use
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- A browser automation tool (Playwright MCP, Selenium, etc.) for platform login verification
|
|
18
|
+
- **If the social-autoposter MCP is connected** (you can see the tools `setup`, `draft_cycle`, `autopilot`, `get_stats`): use the MCP tools. They write config, **seed topics into the DB**, and **auto-detect the X handle** for you. Do NOT hand-edit `config.json`. This is the primary path — follow "MCP path" below.
|
|
19
|
+
- **If only the CLI/skill is installed** (no MCP tools): use the "CLI fallback" at the end.
|
|
21
20
|
|
|
22
21
|
---
|
|
23
22
|
|
|
24
|
-
##
|
|
23
|
+
## MCP path (primary)
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
### Step 1: Interview the user, one question at a time
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
Gather the fields the `setup` tool needs. Ask conversationally, wait for each answer.
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
1. **Website** — "What's the product's website?"
|
|
30
|
+
2. **Description** — "In 1-3 sentences, what does it do?"
|
|
31
|
+
3. **ICP** — "Who's the ideal customer you want to engage on X?"
|
|
32
|
+
4. **Voice** — "What tone should replies have? Any words/claims to avoid?"
|
|
33
|
+
5. **Differentiator** (recommended) — "What makes it different from the alternatives?"
|
|
34
|
+
6. **Search topics** (required) — "What phrases or keywords do your buyers actually tweet about? Give me 5-15, comma-separated." These become the literal X searches the cycle runs. **Without them there is nothing to scan**, so don't skip this.
|
|
35
|
+
7. **Get-started link** (recommended) — "Primary call-to-action link (signup / get started)?"
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
ls ~/social-autoposter/schema-postgres.sql 2>/dev/null && echo "FOUND" || echo "NOT_FOUND"
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
If NOT_FOUND, install:
|
|
37
|
-
```bash
|
|
38
|
-
npx social-autoposter init
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
This copies all scripts, skill files, and config templates to `~/social-autoposter/`. It also:
|
|
42
|
-
- Creates `config.json` from `config.example.json` (if missing)
|
|
43
|
-
- Creates `.env` from `.env.example` (if missing) — includes pre-filled `DATABASE_URL`
|
|
44
|
-
- Installs `psycopg2-binary` (Python driver for Postgres)
|
|
45
|
-
- Symlinks `~/.claude/skills/social-autoposter` → `~/social-autoposter/skill`
|
|
46
|
-
|
|
47
|
-
To update scripts later without touching config/data:
|
|
48
|
-
```bash
|
|
49
|
-
npx social-autoposter update
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Set `SKILL_DIR=~/social-autoposter` for the rest of this wizard.
|
|
37
|
+
For the voice/angle, it helps to draft a short first-person `voice`/`differentiator` from their answers and confirm it reads like them before saving. Aim for specific (names tools, numbers, real experience), not generic.
|
|
53
38
|
|
|
54
|
-
### Step 2:
|
|
39
|
+
### Step 2: Create the project with `setup`
|
|
55
40
|
|
|
56
|
-
|
|
41
|
+
Call the `setup` tool with a short slug `name` plus the fields above. Pass `search_topics` as a comma-separated string or array. You can fill fields incrementally across calls — it merges and reports what's still missing. A project is **ready** only once it has name, website, description, icp, voice, **and search_topics**.
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
source "$SKILL_DIR/.env"
|
|
60
|
-
python3 -c "
|
|
61
|
-
import psycopg2, os
|
|
62
|
-
conn = psycopg2.connect(os.environ['DATABASE_URL'])
|
|
63
|
-
cur = conn.cursor()
|
|
64
|
-
cur.execute(\"SELECT COUNT(*) FROM posts\")
|
|
65
|
-
print('Connected. Posts in DB:', cur.fetchone()[0])
|
|
66
|
-
conn.close()
|
|
67
|
-
"
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Expected: `Connected. Posts in DB: <number>` (any number is fine, including 0).
|
|
71
|
-
|
|
72
|
-
If psycopg2 is missing: `pip3 install psycopg2-binary`
|
|
73
|
-
|
|
74
|
-
If the connection fails, check that `DATABASE_URL` is set in `$SKILL_DIR/.env`.
|
|
43
|
+
When the project becomes ready, `setup` **automatically seeds its `search_topics` into the DB** (`project_search_topics`) and tells you how many it seeded. You do not run any seed script by hand.
|
|
75
44
|
|
|
76
|
-
### Step 3:
|
|
45
|
+
### Step 3: Connect X/Twitter
|
|
77
46
|
|
|
78
|
-
`
|
|
47
|
+
Call `setup` with `action:'connect_x'` (no `confirm`) first — it returns an explanation of what will happen (it imports your x.com/twitter.com cookies into the autoposter's managed Chrome). Relay that to the user, get their OK, then call again with `action:'connect_x', confirm:true`.
|
|
79
48
|
|
|
80
|
-
|
|
49
|
+
This imports the session **and auto-detects + records your real `@handle`** into `config.json` (`accounts.twitter.handle`). That handle scopes attribution, own-reply skipping, and account-keyed operations — so do not hand-edit it to a placeholder.
|
|
81
50
|
|
|
82
|
-
|
|
83
|
-
- "What's your Reddit username?" → set `accounts.reddit.username`
|
|
84
|
-
- Login method is always `browser` (Reddit has no public posting API)
|
|
51
|
+
### Step 4: Verify with a draft cycle
|
|
85
52
|
|
|
86
|
-
**
|
|
87
|
-
- "What's your X handle?" → set `accounts.twitter.handle`
|
|
88
|
-
- Login method is always `browser`
|
|
53
|
+
Run the `draft_cycle` tool. It scans X, drafts replies, and shows them for your approval — it **posts nothing** until you approve. If it comes back empty with a clear reason (e.g. "no search topics"), fix that (re-run `setup` with topics) and try again. A non-empty review form means the pipeline is healthy end-to-end.
|
|
89
54
|
|
|
90
|
-
|
|
91
|
-
- "What's your LinkedIn name?" → set `accounts.linkedin.name`
|
|
92
|
-
- Login method is always `browser`
|
|
55
|
+
### Step 5 (optional): Autopilot
|
|
93
56
|
|
|
94
|
-
|
|
95
|
-
- "Do you want to set up Moltbook? (y/n)"
|
|
96
|
-
- If yes: "What's your Moltbook username?" and "What's your Moltbook API key?"
|
|
97
|
-
- Edit `$SKILL_DIR/.env` and set `MOLTBOOK_API_KEY=<key>` (the file already exists from init)
|
|
98
|
-
- Set `accounts.moltbook.username` and `accounts.moltbook.api_key_env` in `config.json`
|
|
99
|
-
|
|
100
|
-
### Step 4: Configure content
|
|
101
|
-
|
|
102
|
-
This step is the most important one. Take your time. The quality of every future post depends on it.
|
|
57
|
+
If the user wants hands-free posting, call `autopilot` with `action:'enable'` — it loads the background cycle and daily auto-updates. `action:'status'` reports whether it's loaded; `action:'disable'` turns it off (manual `draft_cycle` still works).
|
|
103
58
|
|
|
104
59
|
---
|
|
105
60
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Ask: "Which subreddits do you want to post in? (comma-separated, or press enter for defaults)"
|
|
109
|
-
|
|
110
|
-
Default suggestion: `ClaudeAI, ClaudeCode, programming, webdev, devops`
|
|
111
|
-
|
|
112
|
-
Write the list to `config.json` under `subreddits`.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
**4b. Content angle — interview the user**
|
|
117
|
-
|
|
118
|
-
Don't just ask for a one-liner. Run a short interview to understand who they are, then write the angle for them.
|
|
119
|
-
|
|
120
|
-
Ask these questions one at a time. Wait for each answer before asking the next.
|
|
121
|
-
|
|
122
|
-
**Question 1:** "What are you currently working on or building? Be specific — what does it actually do?"
|
|
123
|
-
|
|
124
|
-
**Question 2:** "What's your technical background? What languages, tools, or domains do you know well?"
|
|
125
|
-
|
|
126
|
-
**Question 3:** "What's something you've learned recently from your work that most people in your field don't know yet — or that surprised you?"
|
|
127
|
-
|
|
128
|
-
**Question 4:** "What's a recurring frustration or problem you've run into that you think others in your community also face?"
|
|
129
|
-
|
|
130
|
-
**Question 5:** "Do you have any unusual setup or workflow? (e.g. running multiple AI agents, building on niche platforms, working solo on something usually done by teams)"
|
|
131
|
-
|
|
132
|
-
After collecting all answers, synthesize them into a `content_angle` that:
|
|
133
|
-
- Is 2-4 sentences
|
|
134
|
-
- Is written in first person
|
|
135
|
-
- Names specific tools, numbers, and experiences (not generic claims)
|
|
136
|
-
- Captures what makes their perspective genuinely different from a typical developer
|
|
61
|
+
## CLI fallback (no MCP)
|
|
137
62
|
|
|
138
|
-
|
|
139
|
-
> "Here's the content angle I'll use to write comments in your voice:
|
|
140
|
-
> [DRAFT]
|
|
141
|
-
> Does this sound like you? Want to change anything?"
|
|
63
|
+
Only if the MCP tools aren't available.
|
|
142
64
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
**
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
**
|
|
149
|
-
> "Building a macOS desktop AI agent that controls the browser and writes code via voice. Running 5 Claude agents in parallel on the same codebase — learned the hard way that they need zero file overlap or everything breaks. API costs hit $800/month before I got aggressive about caching."
|
|
65
|
+
1. **Install**: `npx social-autoposter init` (creates `config.json` from the template and `.env`; symlinks the skill). Update later with `npx social-autoposter update`.
|
|
66
|
+
2. **Configure the project**: edit `~/social-autoposter/config.json` `projects[]` with `name`, `website`, `description`, `icp`, `voice`, and `search_topics` (array). Leave `accounts.twitter.handle` empty — it's filled on connect.
|
|
67
|
+
3. **Seed topics into the DB** (the cycle reads the DB, not config): `python3 scripts/seed_search_topics.py --project <name>`.
|
|
68
|
+
4. **Connect X**: `python3 scripts/setup_twitter_auth.py connect` — imports the session and records the real handle.
|
|
69
|
+
5. **Verify**: `DRAFT_ONLY=1 TWITTER_PAGE_GEN_RATE=0 bash skill/run-twitter-cycle.sh` — drafts without posting; it prints `DRAFT_ONLY_PLAN=<path>` on success.
|
|
70
|
+
6. **Automation** (optional): on macOS, symlink + load the launchd plists in `skill/launchd/`; on Linux, add the matching cron entries.
|
|
150
71
|
|
|
151
72
|
---
|
|
152
73
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
Ask: "Do you have any open source projects or products you'd want to mention naturally when the topic comes up? (y/n)"
|
|
156
|
-
|
|
157
|
-
If yes, for each project run through:
|
|
158
|
-
- "What's the name?"
|
|
159
|
-
- "One sentence: what does it do?"
|
|
160
|
-
- "Website URL? (or leave blank)"
|
|
161
|
-
- "GitHub URL? (or leave blank)"
|
|
162
|
-
- "What topics or keywords would make it relevant to mention? (e.g. 'desktop automation, macOS, accessibility APIs')"
|
|
163
|
-
|
|
164
|
-
After each project, ask: "Any more projects to add? (y/n)"
|
|
165
|
-
|
|
166
|
-
Store each under `config.json` → `projects` array with fields: `name`, `description`, `website`, `github`, `topics` (array of strings).
|
|
167
|
-
|
|
168
|
-
The `topics` keywords are what trigger natural mentions — when someone in a thread mentions one of these topics, the agent knows this project is relevant to bring up.
|
|
169
|
-
|
|
170
|
-
### Step 5: Verify browser logins
|
|
171
|
-
|
|
172
|
-
For each configured platform, verify the user is logged in:
|
|
173
|
-
|
|
174
|
-
**Reddit:**
|
|
175
|
-
- Navigate to `https://old.reddit.com` using browser automation
|
|
176
|
-
- Check if a username appears in the top-right (logged in) or a "login" link (not logged in)
|
|
177
|
-
- If not logged in: "Please log into Reddit in your browser, then say 'done'"
|
|
178
|
-
- Re-check after they confirm
|
|
179
|
-
|
|
180
|
-
**X/Twitter:**
|
|
181
|
-
- Navigate to `https://x.com/home`
|
|
182
|
-
- Check if the home timeline loads (logged in) or a login page appears
|
|
183
|
-
- Same flow if not logged in
|
|
184
|
-
|
|
185
|
-
**LinkedIn:**
|
|
186
|
-
- Navigate to `https://www.linkedin.com/feed/`
|
|
187
|
-
- Check if the feed loads or a login page appears
|
|
188
|
-
|
|
189
|
-
**Moltbook:**
|
|
190
|
-
- Source the env file and test the API key:
|
|
191
|
-
```bash
|
|
192
|
-
source ~/social-autoposter/.env
|
|
193
|
-
curl -s -H "Authorization: Bearer $MOLTBOOK_API_KEY" "https://www.moltbook.com/api/v1/posts?limit=1"
|
|
194
|
-
```
|
|
195
|
-
- Check for a successful response (not an auth error)
|
|
196
|
-
|
|
197
|
-
Report which platforms are ready and which need attention.
|
|
74
|
+
## Summary to show the user
|
|
198
75
|
|
|
199
|
-
### Step 6: Test run (dry run)
|
|
200
|
-
|
|
201
|
-
Run the thread finder to verify everything works:
|
|
202
|
-
```bash
|
|
203
|
-
python3 "$SKILL_DIR/scripts/find_threads.py" --limit 3
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Show the user the candidate threads found. Don't post anything — just verify the pipeline works.
|
|
207
|
-
|
|
208
|
-
### Step 7: Set up automation (optional)
|
|
209
|
-
|
|
210
|
-
Ask: "Do you want posts to run automatically on a schedule? (y/n)"
|
|
211
|
-
|
|
212
|
-
If yes, and on macOS:
|
|
213
|
-
- The launchd plists are already in `$SKILL_DIR/launchd/` (one per platform: reddit-search, reddit-threads, twitter-cycle, linkedin, moltbook, github, plus system jobs: stats, engage, audit, octolens, scan-*, dm-replies-*, link-edit-*)
|
|
214
|
-
- Symlink and load all of them:
|
|
215
|
-
```bash
|
|
216
|
-
for plist in "$SKILL_DIR"/launchd/com.m13v.social-*.plist; do
|
|
217
|
-
ln -sf "$plist" ~/Library/LaunchAgents/
|
|
218
|
-
launchctl load ~/Library/LaunchAgents/$(basename "$plist")
|
|
219
|
-
done
|
|
220
|
-
```
|
|
221
|
-
- Cadences: Twitter cycle every 20 min, Reddit search every 30 min, Reddit threads 4x/day, reply scans 2x/day, engage loop every 4 h, stats refresh 4x/day, DM outreach and link-edit 4x/day.
|
|
222
|
-
|
|
223
|
-
If yes, and on Linux:
|
|
224
|
-
- Generate crontab entries (pick the platforms you use):
|
|
225
|
-
```
|
|
226
|
-
*/20 * * * * cd ~/social-autoposter && bash skill/run-twitter-cycle.sh
|
|
227
|
-
*/30 * * * * cd ~/social-autoposter && bash skill/run-reddit-search.sh
|
|
228
|
-
10 */6 * * * cd ~/social-autoposter && bash skill/run-reddit-threads.sh
|
|
229
|
-
0 */6 * * * cd ~/social-autoposter && bash skill/stats.sh
|
|
230
|
-
0 */4 * * * cd ~/social-autoposter && bash skill/engage.sh
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
If no: "You can run manually anytime with `/social-autoposter`"
|
|
234
|
-
|
|
235
|
-
### Step 8: Summary
|
|
236
|
-
|
|
237
|
-
Read `config.json` accounts and compute each platform's stats URL:
|
|
238
|
-
- Twitter/X handle (strip leading `@`): `https://s4l.ai/stats/HANDLE`
|
|
239
|
-
- Reddit username: `https://s4l.ai/stats/USERNAME`
|
|
240
|
-
- LinkedIn name (URL-encoded spaces as `%20`): `https://s4l.ai/stats/NAME`
|
|
241
|
-
- Moltbook username: `https://s4l.ai/stats/MOLTBOOK_USERNAME`
|
|
242
|
-
|
|
243
|
-
Print a summary with real values substituted:
|
|
244
76
|
```
|
|
245
77
|
Social Autoposter Setup Complete
|
|
246
78
|
|
|
247
|
-
Installed: ~/social-autoposter (
|
|
248
|
-
Database: Postgres (DATABASE_URL in .env)
|
|
79
|
+
Installed: ~/social-autoposter (via npm)
|
|
249
80
|
Config: ~/social-autoposter/config.json
|
|
250
|
-
|
|
251
|
-
Skill: ~/.claude/skills/social-autoposter
|
|
252
|
-
|
|
253
|
-
Platforms:
|
|
254
|
-
Reddit: u/USERNAME ✓
|
|
255
|
-
X/Twitter: @HANDLE ✓
|
|
256
|
-
LinkedIn: NAME ✓
|
|
257
|
-
Moltbook: USERNAME ✓
|
|
258
|
-
|
|
259
|
-
Automation: launchd (hourly post, 6h stats, 2h engage)
|
|
81
|
+
Backend: s4l.ai HTTP API (per-install identity; no local DB)
|
|
260
82
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
LinkedIn: https://s4l.ai/stats/NAME
|
|
265
|
-
Moltbook: https://s4l.ai/stats/MOLTBOOK_USERNAME
|
|
83
|
+
Project: NAME — ready
|
|
84
|
+
Search topics seeded: N
|
|
85
|
+
X/Twitter: @HANDLE (auto-detected on connect)
|
|
266
86
|
|
|
267
|
-
|
|
87
|
+
Verify: draft_cycle (drafts for review, posts nothing)
|
|
88
|
+
Autopilot: autopilot action:'enable' (hands-free)
|
|
89
|
+
Stats: https://s4l.ai/stats/HANDLE
|
|
268
90
|
Update: npx social-autoposter update
|
|
269
91
|
```
|
|
270
92
|
|
|
271
|
-
Tell the user
|
|
93
|
+
Tell the user their stats page (`https://s4l.ai/stats/<handle>`) populates after the first real post.
|