superx-cli 0.1.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,9 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased (joins 0.1.0)
3
+ ## 0.3.0 (2026-09-05)
4
4
 
5
+ - Engage feeds: `engage:feeds` lists the keyword and list feeds saved in the app (with `type`, `active`, and `fetch_units`), and `engage:posts <feedId>` fetches one feed's candidate posts for review (`--account`, `--limit` 1-50, `--mode top|latest`, `--fresh`, `--include-replied`, `--exclude` comma list of post ids to page with). Read-only by design: replies are written and sent by a person in the SuperX app, so there is no reply command. `--limit` applies to keyword feeds; list feeds return one page per fetch and are paged with `--exclude`. Feed fetches have their own per-plan daily allowance and a list feed that rotates its members counts as 3
6
+ - Inspiration search relevance: `inspiration:search` results are now relevance-ranked, strongest matches first and more loosely related posts after. BEHAVIOR CHANGE: results are no longer a fresh varied mix on every run, and weak and promotional matches are filtered out, so a page can return fewer posts than `--limit`; `has_more` no longer requires a full page, so keep paging with `--page` while it is true (pages run 1 to 7). `--sort relevant` is still the default and is the relevance-ranked order
7
+ - Docs: write commands now describe `--account` for linked accounts (server change shipped Sep 4 2026)
8
+
9
+ ## 0.2.0 (2026-08-01)
10
+
11
+ - Advanced post settings: `scheduled:create`/`scheduled:update` gain `--auto-retweet <h>` / `--auto-retweet-remove <h>` (1-12), `--auto-delete <h>` / `--auto-delete-threshold <views>`, `--auto-plug <templateId>` / `--auto-plug-threshold <likes>`, `--super-followers`, plus `--no-*` disable forms; new `plug-templates:list` command. BEHAVIOR CHANGE: posts created via the API now inherit the account's Default Post Settings (auto retweet, auto delete, auto plug, auto DM, Super Followers only) when the matching flags are omitted, exactly like posts composed in the app; previously API posts got no advanced settings at all. Auto DM stays inherit-only (no flag) and surfaces `auto_dm_skipped: true` when a plan limit strips it
5
12
  - Media: `media:upload <file>` uploads a local image (JPG/PNG/WEBP 5MB, GIF 15MB) and prints its `object_key`; `scheduled:create`/`scheduled:update` gain `--media` (comma list of keys), `--alt-text` (single key), and `--parts-json` for threads with per-part media. Text replacement on update is a full replace, media included
6
13
  - Signal agent writes: `signals:create-agent` (--name, --icp, repeatable --keyword, --precision, --list-id, --idempotency-key with replay detection), `signals:pause-agent <id>`, `signals:resume-agent <id>`, `signals:delete-agent <id>`
14
+ - Context settings: `context:get` (the account's AI writing background: profile description, interests, SuperX rules, reply settings, favorite creators, style guide, products), `context:set` (present flags only change; `""` clears a string; `--interests`/`--favorite-creators` comma lists fully replace; boolean flags support `--no-*`), `context:products`, `context:products:set` (upsert by `--url` or edit by `--id`), `context:products:delete <id>`
15
+ - Queue settings: `queue:get` (the account's posting schedule: predefined time slots and their timezone) and `queue:set` (`--slots-json` full replace, max 50, 0 = Sunday, `'[]'` clears; `--timezone` IANA name). Changing the slots also re-flows queued posts onto them the way the app does, reported as `reflow: { moved, skipped, bailed }`; a timezone-only change never moves posts
16
+ - Shared accounts: `accounts` now lists accounts shared with you (manual and team shares) alongside your own, each with `shared` and `permission`. `--account` accepts them everywhere, and `context:*` / `queue:set` can write to them. A share with Editor permission can change queue settings but gets 403 `editor_restricted` on context writes
7
17
 
8
18
  ## 0.1.0 (2026-07-06)
9
19
 
package/PLAYBOOK.md CHANGED
@@ -34,7 +34,7 @@ A good post can go from a handful of follower likes to thousands of stranger vie
34
34
  - Reply early under larger accounts in the niche with something additive (a fact, a sharp take, a smart follow-up). Never "great post" filler.
35
35
  - Time the first window: schedule the best post of the day for when the audience is active, then protect the first 30 to 60 minutes by replying to every response fast. Early engagement compounds.
36
36
 
37
- Actions: `superx scheduled:create --at` for peak-time scheduling; `superx replies:list` to confirm the account is actually participating in conversations, not just broadcasting.
37
+ Actions: `superx scheduled:create --at` for peak-time scheduling; `superx queue:get` to see the account's standing posting slots and `superx queue:set --slots-json ...` to move that cadence onto the hours the audience is actually awake (queued posts follow the slots automatically); `superx replies:list` to confirm the account is actually participating in conversations, not just broadcasting.
38
38
 
39
39
  ## 4. The engagement loop (3-3-3)
40
40
 
@@ -45,7 +45,7 @@ Consistent, targeted interaction grows accounts faster than posting alone:
45
45
  - After a genuine back-and-forth, a soft pointer to related content on the profile is fine. Self-promo inside someone else's thread is not.
46
46
  - Budget: about 20 minutes outbound and 20 minutes inbound daily.
47
47
 
48
- Data: `superx contacts:list --sort engagement` identifies who already engages most (reply to them first); `superx contacts:replies <id>` shows the history with one person so replies can be specific. For the inbound block, `superx replies:received --sort recent` lists every reply the audience has sent across all posts, so nothing goes unanswered. Keep the 3-3-3 circle in a contact list (`superx lists:list`, `lists:add-member`) so the daily targets survive between sessions. If the account runs signal agents, `superx signals:leads` surfaces fresh people matching the ideal customer profile, with the post that revealed them; engage while the discovery is recent. No agent yet? `superx signals:create-agent --name ... --icp ...` sets one up from a plain-language customer description (keywords auto-suggested when omitted); leads accumulate over the following days, so create it early in the week and harvest with `signals:leads` later.
48
+ Data: `superx contacts:list --sort engagement` identifies who already engages most (reply to them first); `superx contacts:replies <id>` shows the history with one person so replies can be specific. For the inbound block, `superx replies:received --sort recent` lists every reply the audience has sent across all posts, so nothing goes unanswered. The Engage feeds saved in the app are another source: `superx engage:feeds` lists them and `superx engage:posts <feedId>` returns the posts one surfaces, ready to score before a person writes and sends the reply in the app. Keep the 3-3-3 circle in a contact list (`superx lists:list`, `lists:add-member`) so the daily targets survive between sessions. If the account runs signal agents, `superx signals:leads` surfaces fresh people matching the ideal customer profile, with the post that revealed them; engage while the discovery is recent. No agent yet? `superx signals:create-agent --name ... --icp ...` sets one up from a plain-language customer description (keywords auto-suggested when omitted); leads accumulate over the following days, so create it early in the week and harvest with `signals:leads` later.
49
49
 
50
50
  ## 5. The research system
51
51
 
@@ -61,10 +61,11 @@ Data: `superx posts:list --sort likes --since <60d ago>` and `--sort impressions
61
61
 
62
62
  A sustainable weekly loop an agent can run:
63
63
 
64
- 1. Confirm the 1-2 topic clusters for the week; every post should fit one.
64
+ 1. Confirm the 1-2 topic clusters for the week; every post should fit one. `superx context:get` shows the account's stated interests, hard rules, profile description, and products: drafts must respect the rules and fit the interests. If the user's focus has shifted, update it (`superx context:set --interests ...`) so every AI surface in the app follows, and get confirmation first: these settings shape ALL future AI output for the account.
65
65
  2. Pull last week's winners (`posts:list --sort likes --since ...`) and note why each worked.
66
66
  3. Plan roughly 7 posts for the week; draft them (`scheduled:create` without `--at`, with a `--title` naming the angle and a `--tag` for the week's cluster so the human can scan the batch), review, then promote the best 3 to peak times (`scheduled:update <id> --at ... --status scheduled`).
67
67
  4. Include one format experiment per week (a thread via `--part`, a longer post, or a long-form X Article via `articles:create` when a topic deserves depth: draft it, add a cover with `articles:cover`, and let the human review before `articles:publish`) so format reach is never left untested.
68
+ For the week's strongest post, consider the advanced settings: `--auto-retweet 6` gives it a second push into a different timezone window, and `--auto-plug <template-id> --auto-plug-threshold 50` (ids from `plug-templates:list`) turns a winner into a lead-in for the account's offer. Posts inherit the account's Default Post Settings automatically when the flags are omitted; use `--no-auto-retweet`/`--no-auto-plug` on posts where the defaults do not fit (see SKILL.md for the full flag set).
68
69
  5. Refresh the 3-3-3 circle (`contacts:list`) and do the daily reply blocks.
69
70
  6. End of week: `posts:analytics` for the trend, top 3 posts by meaningful actions, one failure mode to fix with a rule (for example "no link-drop posts", "never ghost early replies").
70
71
  7. Repurpose one winner into two new assets for next week (tighter version, thread expansion, follow-up take).
package/README.md CHANGED
@@ -6,7 +6,7 @@ npx skills add superx-so/superx-agent
6
6
 
7
7
  # SuperX CLI
8
8
 
9
- **Twitter/X growth CLI for developers and AI agents.** Read your posts and their metrics, pull account analytics, find the people who engage with you most, create draft or scheduled posts and threads (with image attachments), and write, schedule, and publish long-form X Articles (with AI cover generation) through the [SuperX API](https://docs.superx.so).
9
+ **Twitter/X growth CLI for developers and AI agents.** Read your posts and their metrics, pull account analytics, find the people who engage with you most, create draft or scheduled posts and threads (with image attachments), write, schedule, and publish long-form X Articles (with AI cover generation), and read or update the Context settings that steer SuperX's AI writing through the [SuperX API](https://docs.superx.so).
10
10
 
11
11
  Two things ship in this repo:
12
12
 
@@ -27,7 +27,7 @@ Requires Node.js 18 or newer.
27
27
 
28
28
  ## Authentication
29
29
 
30
- Create an API key in the SuperX app: [app.superx.so/account?tab=developers](https://app.superx.so/account?tab=developers)
30
+ Create an API key in the SuperX app: [app.superx.so/account?tab=api](https://app.superx.so/account?tab=api)
31
31
 
32
32
  ### Option 1: Guided login (local use)
33
33
 
@@ -80,7 +80,7 @@ superx me # Key owner, plan tier, key name and scopes
80
80
  superx accounts # X accounts this key can read (main account first)
81
81
  ```
82
82
 
83
- Read commands accept `--account <id>` (an id from `superx accounts`) to select a linked account. Omitting it means the main account.
83
+ Read commands accept `--account <id>` (an id from `superx accounts`) to select a linked or shared account. Write commands accept it too for your main or linked accounts; accounts shared with you by other people are read-only (403 `writes_main_account_only`). Omitting it means the main account.
84
84
 
85
85
  ### Posts
86
86
 
@@ -124,7 +124,7 @@ superx inspiration:search "indie hackers" --sort outlier --min-likes 500
124
124
  superx inspiration:search "AI tools" --min-followers 1000 --max-followers 50000
125
125
  ```
126
126
 
127
- Searches a library of 50M+ real high-performing posts by topic. Options: `--sort relevant|recent|likes|reposts|impressions|outlier`, `--min-likes/--min-reposts/--min-replies/--min-bookmarks/--min-impressions`, `--min-followers/--max-followers` (author size), `--since/--until` (UTC ISO-8601), `--lang` (default en), `--exclude-topics "crypto,politics"`, `--limit` (max 50), `--page` (1-7). Each result carries an `outlier_score`: how far the post outperformed the norm for its author's follower tier. Results are intentionally varied between runs; use them for structures and hooks to remix, never to copy.
127
+ Searches a library of 50M+ real high-performing posts by topic. Options: `--sort relevant|recent|likes|reposts|impressions|outlier`, `--min-likes/--min-reposts/--min-replies/--min-bookmarks/--min-impressions`, `--min-followers/--max-followers` (author size), `--since/--until` (UTC ISO-8601), `--lang` (default en), `--exclude-topics "crypto,politics"`, `--limit` (max 50), `--page` (1-7). Each result carries an `outlier_score`: how far the post outperformed the norm for its author's follower tier. Results are relevance-ranked, strongest matches first, with weak and promotional matches filtered out, so a page may return fewer than `--limit` posts. Use them for structures and hooks to remix, never to copy.
128
128
 
129
129
  ### Contacts (who engages with you)
130
130
 
@@ -142,7 +142,24 @@ superx lists:add-member <list-id> --handle levelsio # or --x-user-id 44196397
142
142
  superx lists:remove-member <list-id> <member-id>
143
143
  ```
144
144
 
145
- Lists are the saved people-collections from the SuperX app. System lists (Followers, Following, Repliers, Reposters) appear in `lists:list` but are read-only and their members are not available through the API (400 `system_list_not_supported`). Adding someone already in a list is harmless: the API returns the existing member with `"duplicate": true` and writes nothing. Member writes are main account only.
145
+ Lists are the saved people-collections from the SuperX app. System lists (Followers, Following, Repliers, Reposters) appear in `lists:list` but are read-only and their members are not available through the API (400 `system_list_not_supported`). Adding someone already in a list is harmless: the API returns the existing member with `"duplicate": true` and writes nothing. Member writes work on your main or linked accounts with `--account`; shared accounts are read-only.
146
+
147
+ ### Engage (feed posts to reply to)
148
+
149
+ ```bash
150
+ superx engage:feeds # the feeds saved in the app's Engage tab
151
+ superx engage:posts <feed-id> --limit 50 # one big page of candidate posts
152
+ superx engage:posts <feed-id> --mode latest --fresh true
153
+ superx engage:posts <feed-id> --exclude 1234567890,1234567891 # next batch, minus what you have
154
+ ```
155
+
156
+ Engage feeds are the keyword and list feeds set up in the SuperX app. `engage:feeds` returns each feed's `id`, `name`, `type` (`keywords`, `list`, or `x_list`), `active` flag, and `fetch_units` (what one fetch of it costs: 1 for a keyword feed, at most 3 for list feeds, and an imported X list charges 1 at fetch time). Feeds are created and edited in the app, not through the API.
157
+
158
+ `engage:posts <feed-id>` returns candidate posts with text, author (handle, bio, follower counts), engagement metrics, and post time, plus `has_more` and the `feed` it came from. Results are for review: replies are written and sent by a person in SuperX, so there is no reply command. Sending replies that read as inauthentic can get an X account suspended under X's inauthentic-behavior rules and a SuperX account terminated; AI output must be reviewed and meaningfully edited by a person before it is posted, and reply activity is logged and may be audited.
159
+
160
+ `--limit` (1-50, default 20) applies to keyword feeds. List feeds ignore it upstream and return one page per fetch, about 10 posts for a member list and 20 to 25 for an imported X list, with `--limit` only trimming that page; page those with `--exclude` instead. On a keyword feed a 50-post page costs the same as a 20-post page, so ask for 50 a few times a day and filter locally rather than polling. Feeds refresh over hours, so fetching more often than hourly returns the same posts. `--mode top|latest` defaults to `top`; `--fresh true` skips the cache; `--include-replied true` keeps posts already replied to, skipped, or blocked and flags them with `replied`. `--exclude` takes up to 100 post ids and is how you page.
161
+
162
+ Feed fetches have their own per-plan daily allowance (Trial 20, Pro 60, Advanced 120, Ultra 300 per day), separate from the read budget; per minute: trial 2, pro 5, advanced 10, ultra 15. A list feed that rotates its members counts as up to 3 fetches, every other feed as 1; `engage:feeds` never touches the allowance. Over the cap you get 429 `rate_limited`: the API returns `remaining_day`; the CLI prints the message and the retry delay. Fetches also run a few at a time across all API users, so a 429 with a retry delay can mean busy rather than out of allowance; wait and retry. Posts a fetch returns count as seen and are demoted in later fetches, in the app as well as the API. An unknown feed id returns 404 `feed_not_found`.
146
163
 
147
164
  ### Signals (automated lead finding)
148
165
 
@@ -152,7 +169,7 @@ superx signals:leads --limit 20 # newest leads across all a
152
169
  superx signals:leads --agent 3 --deposited false # new leads from one agent
153
170
  superx signals:leads --since "2026-07-01T00:00:00Z" # leads discovered since July
154
171
 
155
- # Create an agent (main account only, write scope)
172
+ # Create an agent (write scope; main or linked account via --account)
156
173
  superx signals:create-agent \
157
174
  --name "Build in public founders" \
158
175
  --icp "Indie founders building SaaS in public, sharing MRR and launches" \
@@ -218,7 +235,23 @@ superx scheduled:update <post-id> --clear-tags --clear-title --clear-scratchpad
218
235
 
219
236
  A new `--at` time alone never schedules a draft; pass `--status scheduled` explicitly. CAUTION: replacement text is a full replace, media included. `--text` without `--media` removes any images the post carried; re-list the current `object_key`s (visible in `scheduled:list`) to keep them. Title, scratchpad, tag, and time edits never touch media.
220
237
 
221
- Write constraints in the current API version: main account only; images via `media:upload` (no video); one of `--text`, `--part`, or `--parts-json`. Idempotent replays add `"replayed": true` to the output. Note that drafts have no scheduled time, so `--from/--to` filters exclude them.
238
+ Write constraints in the current API version: main or linked accounts via `--account` (shared accounts are read-only; tags are workspace-wide and main account only); images via `media:upload` (no video); one of `--text`, `--part`, or `--parts-json`. Idempotent replays add `"replayed": true` to the output. Note that drafts have no scheduled time, so `--from/--to` filters exclude them.
239
+
240
+ ### Advanced settings
241
+
242
+ Auto retweet, auto delete, auto plug, and Super Followers only are available as flags on `scheduled:create` and `scheduled:update`.
243
+
244
+ ```bash
245
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --auto-retweet 6 --auto-retweet-remove 4
246
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --auto-delete 8 --auto-delete-threshold 500
247
+ superx plug-templates:list # template ids for --auto-plug
248
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --auto-plug <template-id> --auto-plug-threshold 50
249
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --no-auto-retweet --no-auto-plug
250
+ superx scheduled:update <post-id> --auto-retweet 2 # override on an existing post
251
+ superx scheduled:update <post-id> --no-auto-delete # remove from an existing post
252
+ ```
253
+
254
+ On create, flags you omit inherit your Default Post Settings from the SuperX app (exactly five settings inherit: auto retweet, auto delete, auto plug, auto DM, Super Followers only; other composer defaults like Bluesky cross-posting never apply to API posts); the `--no-*` forms turn a setting off for that post. On update there is no inheritance: passed flags override, omitted flags keep the post's current settings, `--no-*` removes them. Hours are 1-12; `--auto-plug` needs `--auto-plug-threshold` (likes). Auto DM has no flag and always follows your app defaults; if a plan limit strips it at create time the response carries `"auto_dm_skipped": true`. `scheduled:list` shows the applied settings per post.
222
255
 
223
256
  ### Tags
224
257
 
@@ -231,6 +264,46 @@ superx tags:delete <tag-id> # also removes it from every po
231
264
 
232
265
  Tag names are unique (409 `duplicate_name` on collision) and capped at 40 characters.
233
266
 
267
+ ### Context settings (AI writing background)
268
+
269
+ The Context settings are the background SuperX's AI uses when writing for the account: profile description, interests, hard rules, reply settings, favorite creators, style-guide overrides, and products. Editing them changes every AI writing surface in the app.
270
+
271
+ ```bash
272
+ superx context:get # the whole context document
273
+
274
+ # Only the flags you pass change; "" clears a string; lists fully replace
275
+ superx context:set --rules "Never use hashtags. Keep posts under 200 chars."
276
+ superx context:set --profile-description "Indie hacker building SuperX" --profile-description-enabled
277
+ superx context:set --interests "indie hacking,SaaS,AI agents" # replaces the list
278
+ superx context:set --favorite-creators "levelsio,marc_louvion" # max 3, replaces the list
279
+ superx context:set --reply-rules "Be helpful, never salesy" --no-reply-author-name
280
+ superx context:set --style-audience "Bootstrapped SaaS founders" # outranks the generated guide
281
+ superx context:set --style-audience "" # revert to the generated guide
282
+
283
+ # Products (max 5): mentioned naturally in generated content
284
+ superx context:products
285
+ superx context:products:set --url "https://superx.so" --name "SuperX" --description "X growth platform"
286
+ superx context:products:set --id 3 --updates "Shipped the public API"
287
+ superx context:products:delete <product-id>
288
+ ```
289
+
290
+ Caps: profile description, rules, and reply rules 500 characters; style audience 600; style vocabulary 1000; 30 interests of 50 characters each; 3 favorite creators; 5 products. `context:get` also returns the read-only generated style guide (`style_guide.generated`). `context:products:set --url` creates the product when it does not exist; removing a product is reversible by re-adding the same url. Writes need a key with the write scope, work on any linked or shared account via `--account`, and return 403 `editor_restricted` on a share with Editor permission.
291
+
292
+ ### Queue settings (posting schedule)
293
+
294
+ The posting schedule is the set of predefined time slots the queue fills, plus the timezone they run in: the Edit Queue modal in the app.
295
+
296
+ ```bash
297
+ superx queue:get # slots, timezone, and the is_default flags
298
+
299
+ # Slots are JSON so weekday sets stay unambiguous; 0 = Sunday
300
+ superx queue:set --slots-json '[{"time":"09:00","days":[1,2,3,4,5]},{"time":"17:30","days":[1,3,5]}]'
301
+ superx queue:set --timezone "Europe/London" # never moves queued posts
302
+ superx queue:set --slots-json '[]' # clear every predefined slot
303
+ ```
304
+
305
+ Slots are a full replace, max 50, one entry per unique time. Changing them also re-flows the queue the way the app does: a queued post sitting exactly on an old slot moves to the matching new slot, so gaps are preserved and hand-picked custom times stay put. The response carries `reflow: { moved, skipped, bailed }`; `bailed: true` means the settings were saved but the queue was left untouched on purpose, and rerunning the same command is safe. Changing the timezone and the slots in one call usually moves nothing, because the existing posts were placed under the old timezone; to re-flow them, change the timezone first, then send the slots in a second call. Writes need a key with the write scope and work on any linked or shared account via `--account`, Editor-permission shares included.
306
+
234
307
  ### Articles (long-form X posts)
235
308
 
236
309
  Article bodies are **markdown in both directions**: headings (h1-h3), bullet and numbered lists (one nesting level), blockquotes, bold/italic/strikethrough, links, images by URL, and bare X post URLs as embeds. Code blocks and horizontal rules are not supported by the X Articles format and degrade to plain text (the response lists any degradations in `warnings`).
@@ -324,6 +397,8 @@ The CLI talks to these SuperX API endpoints (base `https://api.superx.so/v1`):
324
397
  | `/signals/agents/:id` | PATCH | `signals:pause-agent <id>` / `signals:resume-agent <id>` |
325
398
  | `/signals/agents/:id` | DELETE | `signals:delete-agent <id>` |
326
399
  | `/signals/leads` | GET | `signals:leads` |
400
+ | `/engage/feeds` | GET | `engage:feeds` |
401
+ | `/engage/feeds/:id/posts` | GET | `engage:posts <feedId>` |
327
402
  | `/media` | POST | `media:upload <file>` |
328
403
  | `/scheduled-posts` | GET | `scheduled:list` |
329
404
  | `/scheduled-posts` | POST | `scheduled:create` |
@@ -342,6 +417,12 @@ The CLI talks to these SuperX API endpoints (base `https://api.superx.so/v1`):
342
417
  | `/articles/:id/schedule` | POST | `articles:schedule <id>` |
343
418
  | `/articles/:id/unschedule` | POST | `articles:unschedule <id>` |
344
419
  | `/articles/:id/cover` | POST | `articles:cover <id>` |
420
+ | `/context` | GET | `context:get`, `context:products` |
421
+ | `/context` | PATCH | `context:set` |
422
+ | `/context/products/:id` | PATCH | `context:products:set` |
423
+ | `/context/products/:id` | DELETE | `context:products:delete <id>` |
424
+ | `/queue-settings` | GET | `queue:get` |
425
+ | `/queue-settings` | PATCH | `queue:set` |
345
426
  | `/docs` | GET | `docs` (no auth) |
346
427
 
347
428
  Full API reference: [docs.superx.so](https://docs.superx.so)
@@ -367,7 +448,8 @@ Exit code `0` = success, `1` = error. Error codes come straight from the API:
367
448
  |------|---------|
368
449
  | `invalid_api_key` (401) | Bad or revoked key; run `superx login` again |
369
450
  | `insufficient_scope` (403) | Read-only key used for a write |
370
- | `writes_main_account_only` (403) | `scheduled:create` with a linked account |
451
+ | `writes_main_account_only` (403) | `scheduled:create` with a linked or shared account |
452
+ | `editor_restricted` (403) | `context:set` or `context:products:*` on a share with Editor permission |
371
453
  | `subscription_required` (403) | SuperX subscription lapsed |
372
454
  | `account_not_found` (404) | `--account` id is not one of your accounts |
373
455
  | `list_not_found` (404) | List id is not one of your contact lists |
@@ -413,9 +495,12 @@ src/
413
495
  ├── contacts.ts # contacts:list / contacts:replies
414
496
  ├── lists.ts # lists:list / lists:members / lists:add-member / lists:remove-member
415
497
  ├── signals.ts # signals:agents / signals:leads / signals:create-agent / signals:pause-agent / signals:resume-agent / signals:delete-agent
498
+ ├── engage.ts # engage:feeds / engage:posts
416
499
  ├── media.ts # media:upload
417
500
  ├── scheduled.ts # scheduled:list / scheduled:create / scheduled:update / scheduled:delete
418
501
  ├── tags.ts # tags:list / tags:create / tags:update / tags:delete
502
+ ├── context.ts # context:get / context:set / context:products / context:products:set / context:products:delete
503
+ ├── queue.ts # queue:get / queue:set
419
504
  ├── articles.ts # articles:list/get/create/update/delete/publish/schedule/unschedule/cover
420
505
  └── docs.ts # docs
421
506
  ```
@@ -448,18 +533,20 @@ superx lists:list
448
533
  superx lists:members <list-id> --q "founder"
449
534
  superx signals:agents
450
535
  superx signals:leads --agent 3 --deposited false
536
+ superx engage:feeds
537
+ superx engage:posts <feed-id> --limit 50
451
538
 
452
- # Contact list writes (main account)
539
+ # Contact list writes (main or linked account)
453
540
  superx lists:add-member <list-id> --handle levelsio
454
541
  superx lists:remove-member <list-id> <member-id>
455
542
 
456
- # Signal agent writes (main account)
543
+ # Signal agent writes (main or linked account)
457
544
  superx signals:create-agent --name "..." --icp "..." --keyword "..."
458
545
  superx signals:pause-agent <id>
459
546
  superx signals:resume-agent <id>
460
547
  superx signals:delete-agent <id>
461
548
 
462
- # Writes (main account)
549
+ # Writes (main or linked account via --account)
463
550
  superx scheduled:create --text "Post" # Draft
464
551
  superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" # Scheduled
465
552
  superx scheduled:create --part "1/" --part "2/" --at "..." # Thread
@@ -478,6 +565,19 @@ superx tags:create "Launch week" --color amber
478
565
  superx tags:update <id> --name "Launch"
479
566
  superx tags:delete <id>
480
567
 
568
+ # Context settings (AI writing background)
569
+ superx context:get
570
+ superx context:set --rules "Never use hashtags."
571
+ superx context:set --interests "indie hacking,SaaS" # replaces the list
572
+ superx context:products
573
+ superx context:products:set --url "https://superx.so" --name "SuperX"
574
+ superx context:products:delete <id>
575
+
576
+ # Queue settings (posting schedule; 0 = Sunday)
577
+ superx queue:get
578
+ superx queue:set --slots-json '[{"time":"09:00","days":[1,3,5]}]' # replaces the slots
579
+ superx queue:set --timezone "Europe/London" # never moves posts
580
+
481
581
  # Articles (markdown bodies; publish needs X Premium)
482
582
  superx articles:create --title "My article" --file draft.md
483
583
  superx articles:list --status draft
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: superx
3
- description: SuperX is a Twitter/X growth tool. Use it to read an account's published posts with engagement metrics, pull account analytics (impressions, likes, replies, follower change), find the people who engage with the account most, review reply history in both directions (sent and received), manage contact lists, create and manage signal agents (automated lead finders) and review the leads they discover, search a library of 50M+ high-performing posts for inspiration, create, edit, tag, or schedule draft posts and threads (with image attachments), and write, schedule, publish, and generate AI covers for long-form X Articles through the SuperX API.
3
+ description: SuperX is a Twitter/X growth tool. Use it to read an account's published posts with engagement metrics, pull account analytics (impressions, likes, replies, follower change), find the people who engage with the account most, review reply history in both directions (sent and received), manage contact lists, create and manage signal agents (automated lead finders) and review the leads they discover, search a library of 50M+ high-performing posts for inspiration, read the Engage feed posts a saved feed surfaces for review, create, edit, tag, or schedule draft posts and threads (with image attachments), write, schedule, publish, and generate AI covers for long-form X Articles, and read or update the account's Context settings (profile description, interests, SuperX rules, reply settings, favorite creators, style guide, products) that steer SuperX's AI writing, all through the SuperX API.
4
4
  homepage: https://docs.superx.so
5
5
  metadata: {"openclaw":{"emoji":"🚀","requires":{"bins":["superx"],"env":[]}}}
6
6
  ---
@@ -21,18 +21,18 @@ official website: https://superx.so
21
21
  | Property | Value |
22
22
  |----------|-------|
23
23
  | **name** | superx |
24
- | **description** | Twitter/X growth CLI: posts, analytics, contacts, contact lists, audience replies, signal agents and their leads, inspiration, tags, scheduling (with images), and long-form Articles via the SuperX API |
24
+ | **description** | Twitter/X growth CLI: posts, analytics, contacts, contact lists, audience replies, signal agents and their leads, inspiration, tags, scheduling (with images), long-form Articles, and Context settings (AI writing background) via the SuperX API |
25
25
  | **allowed-tools** | Bash(superx:*) |
26
26
 
27
27
  ---
28
28
 
29
29
  ## Three Hard Rules (Read First)
30
30
 
31
- **Rule 1: Run `superx status` before anything else.** Every other command fails without valid credentials. If the `superx` binary is missing, install it with `npm install -g superx-cli`. If not authenticated, either run `superx login` (interactive) or set `export SUPERX_API_KEY=sxk_...` (CI and non-interactive sessions). Keys are created at https://app.superx.so/account?tab=developers.
31
+ **Rule 1: Run `superx status` before anything else.** Every other command fails without valid credentials. If the `superx` binary is missing, install it with `npm install -g superx-cli`. If not authenticated, either run `superx login` (interactive) or set `export SUPERX_API_KEY=sxk_...` (CI and non-interactive sessions). Keys are created at https://app.superx.so/account?tab=api.
32
32
 
33
33
  **Rule 2: Read PLAYBOOK.md before creating any content.** This repo ships a growth strategy guide (`PLAYBOOK.md`, also inside the installed npm package). It tells you WHAT to post, WHEN, and WHY: the action hierarchy, out-of-network discovery, the engagement loop, and the failure modes that kill reach. The CLI gives you data and actions; the playbook gives you judgment. Do not schedule content without it.
34
34
 
35
- **Rule 3: Know the write constraints.** `scheduled:create` without `--at` creates a DRAFT (nothing publishes). With `--at` it schedules for that time. `scheduled:update` changes only the flags you pass, and a new `--at` alone never schedules a draft; add `--status scheduled` to promote. Writes work on the main account only. Images attach via `media:upload` then `--media` (JPG/PNG/WEBP up to 5MB, GIF up to 15MB; max 4 images or 1 GIF per post); video is not supported. Timestamps MUST be UTC ISO-8601 with an explicit `Z` or offset; naive timestamps are rejected with 400. `articles:publish` posts a long-form article to X IMMEDIATELY and irreversibly; treat it like hitting Publish in public and get human confirmation unless the user already gave it.
35
+ **Rule 3: Know the write constraints.** `scheduled:create` without `--at` creates a DRAFT (nothing publishes). With `--at` it schedules for that time. `scheduled:update` changes only the flags you pass, and a new `--at` alone never schedules a draft; add `--status scheduled` to promote. Writes work on your main account or any linked account (pass the same `--account` you used to read it); accounts shared with you by other people are read-only, and tags are workspace-wide. Images attach via `media:upload` then `--media` (JPG/PNG/WEBP up to 5MB, GIF up to 15MB; max 4 images or 1 GIF per post); video is not supported. Timestamps MUST be UTC ISO-8601 with an explicit `Z` or offset; naive timestamps are rejected with 400. `articles:publish` posts a long-form article to X IMMEDIATELY and irreversibly; treat it like hitting Publish in public and get human confirmation unless the user already gave it.
36
36
 
37
37
  ---
38
38
 
@@ -103,7 +103,7 @@ superx me # Key owner, plan tier, key name and scopes
103
103
  superx accounts # X accounts this key can read; use ids with --account
104
104
  ```
105
105
 
106
- Reads accept `--account <id>` to select a linked account. Omitting it means the main account.
106
+ Reads accept `--account <id>` to select a linked or shared account. Omitting it means the main account.
107
107
 
108
108
  ### Posts and analytics
109
109
 
@@ -133,7 +133,7 @@ superx inspiration:search "AI tools" --min-likes 500 --min-followers 1000 --max-
133
133
  - Searches a library of 50M+ real high-performing posts. Use results for structures, hooks, and angles to remix. Never copy them.
134
134
  - Flags: `--sort relevant|recent|likes|reposts|impressions|outlier`, `--min-likes/--min-reposts/--min-replies/--min-bookmarks/--min-impressions`, `--min-followers/--max-followers` (author size), `--since/--until`, `--lang` (default en), `--exclude-topics "crypto,politics"`, `--limit` (max 50), `--page` (1-7).
135
135
  - `outlier_score` on each result = how far the post outperformed the norm for its author's follower tier. Sorting by `outlier` surfaces content that won on substance, not audience size.
136
- - Results are intentionally varied between runs; re-running the same query returns a different mix.
136
+ - Results are relevance-ranked, strongest matches first. Weak and promotional matches are filtered out, so a page may return fewer than `--limit` posts.
137
137
 
138
138
  ### Contacts (who engages with you)
139
139
 
@@ -157,7 +157,25 @@ superx lists:remove-member <list-id> <member-id> # member-id from lists:memb
157
157
  - Lists are the saved people-collections from the SuperX app. Use them to track prospects, customers, or people worth engaging.
158
158
  - System lists (Followers, Following, Repliers, Reposters) appear in `lists:list` with `is_system: true` but are read-only and their members are NOT available through the API.
159
159
  - Adding someone already in a list is harmless: the existing member returns with `"duplicate": true` and nothing changes.
160
- - Member writes are main account only and need a key with the write scope.
160
+ - Member writes work on your main or linked accounts (`--account`) and need a key with the write scope; shared accounts are read-only.
161
+
162
+ ### Engage (feed posts to reply to)
163
+
164
+ ```bash
165
+ superx engage:feeds # feeds saved in the app, with type and fetch cost
166
+ superx engage:posts <feed-id> --limit 50 # one big page of candidate posts
167
+ superx engage:posts <feed-id> --mode latest --fresh true # newest, skipping the cache
168
+ superx engage:posts <feed-id> --exclude 1234567890,1234567891 # next batch, minus what you have
169
+ superx engage:posts <feed-id> --include-replied true # keep posts already replied to
170
+ ```
171
+
172
+ - Engage feeds are the keyword and list feeds the user set up in the app's Engage tab. `engage:feeds` gives each feed's `id`, `name`, `type` (`keywords`, `list`, `x_list`), `active`, and `fetch_units`. Feeds are created and edited in the app; the API only reads them.
173
+ - `engage:posts` returns candidate posts (text, author handle/bio/follower counts, engagement metrics, post time) plus `has_more` and the `feed`. Score and shortlist them for the user.
174
+ - READ-ONLY BY DESIGN: there is no reply command. Replies are written and sent by a person in SuperX. Sending replies that read as inauthentic can get an X account suspended and a SuperX account terminated; AI output must be reviewed and meaningfully edited by a person before it is posted. Surface candidates and draft suggestions for the user; never claim a reply was sent.
175
+ - `--limit` (1-50, default 20) applies to KEYWORD feeds. List feeds return one page per fetch (about 10 posts for a member list, 20 to 25 for an imported X list) and `--limit` only trims it. Page a list feed with `--exclude` (at most 100 ids per call; window the list to the most recent ids), not a bigger `--limit`.
176
+ - A 50-post page on a keyword feed costs the same as a 20-post page: ask for `--limit 50` a few times a day and filter locally. Feeds refresh over hours, so polling more often than hourly returns the same posts.
177
+ - Each plan has a daily feed-fetch allowance (Trial 20, Pro 60, Advanced 120, Ultra 300), separate from the read budget; per minute: trial 2, pro 5, advanced 10, ultra 15. A list feed that rotates its members counts as 3 fetches. `engage:feeds` never spends it. Over the cap you get 429 `rate_limited`: the API returns `remaining_day`; the CLI prints the message and the retry delay.
178
+ - Posts a fetch returns count as seen and get demoted in later fetches, in the app as well as here. An unknown feed id returns 404 `feed_not_found`.
161
179
 
162
180
  ### Signals (automated lead finding)
163
181
 
@@ -167,7 +185,7 @@ superx signals:leads --limit 20 # newest leads across all a
167
185
  superx signals:leads --agent 3 --deposited false # one agent's leads not yet in its list
168
186
  superx signals:leads --since "2026-07-01T00:00:00Z" # leads discovered since July
169
187
 
170
- # Create an agent (main account only, write scope)
188
+ # Create an agent (write scope; main or linked account via --account)
171
189
  superx signals:create-agent \
172
190
  --name "Build in public founders" \
173
191
  --icp "Indie founders building SaaS in public, sharing MRR and launches" \
@@ -244,6 +262,37 @@ superx scheduled:update <post-id> --clear-tags --clear-title --clear-scratchpad
244
262
  - A new `--at` alone never schedules a draft. Promotion is always explicit via `--status scheduled` (which needs a future time, provided or already set).
245
263
  - CAUTION: replacement text is a FULL replace, media included. `--text` without `--media` REMOVES any images the post carried; re-list the current `object_key`s (visible in `scheduled:list`) to keep them. Title, scratchpad, tag, and time edits never touch media.
246
264
 
265
+ ### Advanced settings (auto retweet, auto delete, auto plug, super followers)
266
+
267
+ ```bash
268
+ # Explicit values on create
269
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
270
+ --auto-retweet 6 --auto-retweet-remove 4
271
+
272
+ # Auto plug: reply with a template once the post hits a likes threshold
273
+ superx plug-templates:list # id, text, has_media
274
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
275
+ --auto-plug <template-id> --auto-plug-threshold 50
276
+
277
+ # Auto delete underperformers (delete after 8h if under 500 views)
278
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
279
+ --auto-delete 8 --auto-delete-threshold 500
280
+
281
+ # Turn the user's defaults OFF for one post
282
+ superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
283
+ --no-auto-retweet --no-auto-plug
284
+
285
+ # Edit or remove on an existing post (no inheritance on update)
286
+ superx scheduled:update <post-id> --auto-retweet 2
287
+ superx scheduled:update <post-id> --no-auto-delete
288
+ ```
289
+
290
+ - On `scheduled:create`, flags you OMIT inherit the user's Default Post Settings from the SuperX app; that is the expected behavior, not a bug. Exactly five settings inherit (auto retweet, auto delete, auto plug, auto DM, Super Followers only); other composer defaults like Bluesky cross-posting never apply to API posts. Use the `--no-*` forms to turn a default off for one post.
291
+ - On `scheduled:update` there is no inheritance: passed flags override, omitted flags keep the post's current settings, `--no-*` removes them.
292
+ - Hours are 1-12. `--auto-plug` needs `--auto-plug-threshold` (likes); template ids come from `plug-templates:list`, unknown ids fail with `unknown_plug_template`. `--super-followers` / `--no-super-followers` toggle Super Followers only.
293
+ - Auto DM has no flag: it always follows the user's app defaults. If a plan limit strips it at create time, the response carries `"auto_dm_skipped": true`; relay that to the user instead of ignoring it.
294
+ - `scheduled:list` shows the applied settings per post (`auto_retweet`, `auto_delete`, `auto_plug`, `auto_dm`, `super_followers_only`), so you can verify what a post will actually do.
295
+
247
296
  ### Tags
248
297
 
249
298
  ```bash
@@ -255,6 +304,56 @@ superx tags:delete <tag-id> # also removes it from every po
255
304
 
256
305
  Tag names are unique per workspace (409 `duplicate_name`) and capped at 40 characters. Assign tags with `scheduled:create --tag` or `scheduled:update --tag`.
257
306
 
307
+ ### Context settings (AI writing background)
308
+
309
+ The Context settings are the background SuperX's AI uses when writing for the account: who the user is, what they post about, hard rules, whose style they admire, and what products they sell. Editing them changes every AI writing surface in the app.
310
+
311
+ ```bash
312
+ superx context:get # The whole context document
313
+ superx context:get | jq '.data.rules' # One section
314
+
315
+ # Only the flags you pass change; "" clears a string; lists fully replace
316
+ superx context:set --rules "Never use hashtags. Keep posts under 200 chars."
317
+ superx context:set --profile-description "Indie hacker building SuperX" --profile-description-enabled
318
+ superx context:set --interests "indie hacking,SaaS,AI agents" # replaces the list
319
+ superx context:set --favorite-creators "levelsio,marc_louvion" # max 3, replaces the list
320
+ superx context:set --reply-rules "Be helpful, never salesy" --no-reply-author-name
321
+ superx context:set --style-audience "Bootstrapped SaaS founders" # outranks the generated guide
322
+ superx context:set --style-audience "" # revert to the generated guide
323
+
324
+ # Products (max 5): mentioned naturally in generated content
325
+ superx context:products
326
+ superx context:products:set --url "https://superx.so" --name "SuperX" --description "X growth platform"
327
+ superx context:products:set --id 3 --updates "Shipped the public API"
328
+ superx context:products:delete <product-id>
329
+ ```
330
+
331
+ - What each setting affects: `--profile-description` grounds the AI's voice and personalizes the daily content mix and search; `--rules` are mandatory instructions on EVERY AI surface; `--reply-rules` and `--reply-author-name` steer generated replies; `--favorite-creators` (X usernames, max 3) inspire the writing style; `--interests` are the highest-priority topics for content suggestions; `--style-audience`/`--style-vocabulary` outrank the app's generated style guide until cleared.
332
+ - `context:get` also returns the read-only generated style guide (`style_guide.generated`) so you can see what a cleared override falls back to.
333
+ - Caps: profile description 500, rules 500, reply rules 500, style audience 600, style vocabulary 1000 characters; 30 interests of 50 characters each; 3 favorite creators; 5 products.
334
+ - `context:products:set --url` creates the product when it does not exist. Removing a product is reversible: re-adding the same url restores its scraped details.
335
+ - Writes need a key with the write scope. Unlike scheduling, context writes work on ANY linked or shared account via `--account` (they are per-account settings). On a share with Editor permission they return 403 `editor_restricted`: only the account owner can change these. These settings shape ALL future AI output for the account; confirm with the user before changing rules or the profile description.
336
+
337
+ ### Queue settings (posting schedule)
338
+
339
+ The posting schedule is the set of predefined time slots the queue fills, plus the timezone they run in.
340
+
341
+ ```bash
342
+ superx queue:get # slots, timezone, is_default flags
343
+
344
+ # Slots are JSON so weekday sets stay unambiguous; 0 = Sunday
345
+ superx queue:set --slots-json '[{"time":"09:00","days":[1,2,3,4,5]},{"time":"17:30","days":[1,3,5]}]'
346
+ superx queue:set --timezone "Europe/London" # never moves queued posts
347
+ superx queue:set --slots-json '[]' # clear every predefined slot
348
+ ```
349
+
350
+ - `--slots-json` is a FULL REPLACE: max 50 entries, one per unique time, each with at least one weekday. Send the complete set the user should end up with.
351
+ - Changing the slots also re-flows the queue the way the app does: a queued post sitting exactly on an old slot moves to the matching new slot (Nth old occurrence to Nth new occurrence), so gaps are preserved and hand-picked custom times stay put. Read `reflow.moved` in the response to see how many posts moved.
352
+ - `reflow.bailed: true` means the settings were saved but the queue was deliberately left alone (a post had nowhere to land, or the move set was too large). Rerunning the same command is safe.
353
+ - A timezone-only change never moves posts. Changing the timezone and the slots in one call usually moves nothing, because the existing posts were placed under the old timezone; to re-flow them, change the timezone first, then send the slots in a second call.
354
+ - `slots_are_default` / `timezone_is_default` mark values the account has never set; SuperX is using its own default.
355
+ - Writes need a key with the write scope and work on any linked or shared account via `--account`, Editor-permission shares included (running the queue is exactly what a delegate is there for).
356
+
258
357
  ### Articles (long-form X posts)
259
358
 
260
359
  Article bodies are markdown in BOTH directions: headings (h1-h3), bullet and numbered lists (one nesting level), blockquotes, bold/italic/strikethrough, links, images by URL, and bare X post URLs alone on a line as embeds. Code blocks and `---` rules degrade to plain text; the response lists degradations in `warnings`.
@@ -365,7 +464,7 @@ superx scheduled:list --status scheduled
365
464
  1. **Naive timestamps are rejected (400)**. Always include `Z` or an offset: `2026-08-01T15:00:00Z`, not `2026-08-01T15:00:00`.
366
465
  2. **Schedule window**: `--at` must be at least 60 seconds in the future and within 18 months.
367
466
  3. **Read-only keys cannot write**: `scheduled:create`/`scheduled:delete` with a read-only key returns 403 `insufficient_scope`. Check `superx me` for the key's scopes.
368
- 4. **Writes are main-account-only**: passing a linked account to `scheduled:create` returns 403 `writes_main_account_only`. Reads accept any owned account.
467
+ 4. **Writes are main-account-only**: passing a linked or shared account to `scheduled:create` returns 403 `writes_main_account_only`. Reads accept any account `superx accounts` lists. The exceptions are `context:*` and `queue:set`, which are per-account settings.
369
468
  5. **Images need an upload first**: `--media` takes `object_key`s from `media:upload`, never file paths or URLs. Unknown keys return 400 `invalid_media`; a presign whose bytes were never PUT returns 400 `media_not_uploaded`. Video is not supported.
370
469
  6. **Size caps**: max 25 thread parts, 25,000 characters total.
371
470
  7. **Rate limited (429)**: `rate_limited` on stderr with a retry delay. Back off; do not hammer.
@@ -387,6 +486,10 @@ superx scheduled:list --status scheduled
387
486
  23. **Signal agents find leads asynchronously**: `signals:create-agent` returns the created agent, not leads. Leads land over the following minutes and days; read them with `signals:leads`.
388
487
  24. **Plan caps on agents return 403 `cap_reached`**: the plan allows only so many agents (and keyword signals per agent). Pause/delete an existing agent or ask the account owner to upgrade.
389
488
  25. **Agent creation is composite**: with an auto-created list, a mid-failure can leave an empty `Leads: ...` contact list behind (visible in `lists:list`, deletable in the app). The agent itself is never left without signals.
489
+ 26. **`context:set` list flags REPLACE the stored list**: `--interests` and `--favorite-creators` overwrite what is there; include every value the user should keep. `""` on a string flag clears it (style-guide overrides then revert to the generated guide). These settings steer all future AI output; confirm with the user before changing them.
490
+ 27. **`queue:set --slots-json` REPLACES the whole schedule** and re-flows queued posts onto the new slots. Read the current slots with `queue:get` first and send the full set. `'[]'` clears every slot and leaves the queue all-custom. `reflow.bailed: true` means the settings saved but no post moved.
491
+ 28. **`editor_restricted` (403)**: the account is shared with the key owner with Editor permission. Editors can change queue settings but not context settings. Only the account owner can.
492
+ 29. **`engage:posts --limit` is keyword-feeds only**: list feeds return one page of about 10 to 25 posts per fetch, so page them with `--exclude` (the ids you already have, at most 100 per call), not a bigger `--limit`. Each plan also has a daily feed-fetch allowance (separate from reads) and a list feed that rotates its members counts as 3 fetches, so fetch big pages a few times a day rather than polling. Posts a fetch returns count as seen and are demoted in later fetches, in the app as well as here.
390
493
 
391
494
  ---
392
495
 
@@ -417,18 +520,20 @@ superx lists:list
417
520
  superx lists:members <list-id> --q "founder"
418
521
  superx signals:agents
419
522
  superx signals:leads --agent 3 --deposited false
523
+ superx engage:feeds
524
+ superx engage:posts <feed-id> --limit 50
420
525
 
421
- # Contact list writes (main account only)
526
+ # Contact list writes (main or linked account)
422
527
  superx lists:add-member <list-id> --handle levelsio
423
528
  superx lists:remove-member <list-id> <member-id>
424
529
 
425
- # Signal agent writes (main account only)
530
+ # Signal agent writes (main or linked account)
426
531
  superx signals:create-agent --name "..." --icp "..." --keyword "..." # Lead finder
427
532
  superx signals:pause-agent <id>
428
533
  superx signals:resume-agent <id>
429
534
  superx signals:delete-agent <id>
430
535
 
431
- # Writes (main account only)
536
+ # Writes (main or linked account via --account)
432
537
  superx scheduled:create --text "Post" # Draft
433
538
  superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" # Scheduled
434
539
  superx scheduled:create --part "1/" --part "2/" --at "..." # Thread
@@ -459,6 +564,19 @@ superx articles:publish <id>
459
564
  superx articles:cover <id> --style "minimal"
460
565
  superx articles:delete <id>
461
566
 
567
+ # Context settings (AI writing background)
568
+ superx context:get
569
+ superx context:set --rules "Never use hashtags."
570
+ superx context:set --interests "indie hacking,SaaS" # replaces the list
571
+ superx context:products
572
+ superx context:products:set --url "https://superx.so" --name "SuperX"
573
+ superx context:products:delete <id>
574
+
575
+ # Queue settings (posting schedule; 0 = Sunday)
576
+ superx queue:get
577
+ superx queue:set --slots-json '[{"time":"09:00","days":[1,3,5]}]' # replaces the slots
578
+ superx queue:set --timezone "Europe/London" # never moves posts
579
+
462
580
  # Docs and help
463
581
  superx docs # API quickstart (markdown)
464
582
  superx --help # All commands
package/dist/index.js CHANGED
@@ -184,6 +184,13 @@ var SuperXAPI = class {
184
184
  async deleteSignalAgent(id) {
185
185
  return (await this.request(`/signals/agents/${id}`, { method: "DELETE" })).json;
186
186
  }
187
+ // --- Engage ---
188
+ async listEngageFeeds(query = {}) {
189
+ return (await this.request("/engage/feeds", { query })).json;
190
+ }
191
+ async getEngageFeedPosts(feedId, query = {}) {
192
+ return (await this.request(`/engage/feeds/${encodeURIComponent(feedId)}/posts`, { query })).json;
193
+ }
187
194
  // --- Scheduled posts ---
188
195
  async listScheduled(query = {}) {
189
196
  return (await this.request("/scheduled-posts", { query })).json;
@@ -200,6 +207,30 @@ var SuperXAPI = class {
200
207
  async deleteScheduled(id) {
201
208
  return (await this.request(`/scheduled-posts/${encodeURIComponent(id)}`, { method: "DELETE" })).json;
202
209
  }
210
+ // --- Plug templates ---
211
+ async listPlugTemplates(query = {}) {
212
+ return (await this.request("/plug-templates", { query })).json;
213
+ }
214
+ // --- Context settings ---
215
+ async getContext(query = {}) {
216
+ return (await this.request("/context", { query })).json;
217
+ }
218
+ async updateContext(body) {
219
+ return (await this.request("/context", { method: "PATCH", body })).json;
220
+ }
221
+ async updateContextProduct(idOrUrl, body) {
222
+ return (await this.request(`/context/products/${encodeURIComponent(idOrUrl)}`, { method: "PATCH", body })).json;
223
+ }
224
+ async deleteContextProduct(id, query = {}) {
225
+ return (await this.request(`/context/products/${encodeURIComponent(id)}`, { method: "DELETE", query })).json;
226
+ }
227
+ // --- Queue settings ---
228
+ async getQueueSettings(query = {}) {
229
+ return (await this.request("/queue-settings", { query })).json;
230
+ }
231
+ async updateQueueSettings(body) {
232
+ return (await this.request("/queue-settings", { method: "PATCH", body })).json;
233
+ }
203
234
  // --- Media ---
204
235
  async createMediaUpload(body) {
205
236
  return (await this.request("/media", { method: "POST", body })).json;
@@ -324,7 +355,7 @@ function getConfig() {
324
355
  process.stderr.write("Not authenticated. Either:\n");
325
356
  process.stderr.write(" 1. Run: superx login\n");
326
357
  process.stderr.write(" 2. Or set: export SUPERX_API_KEY=sxk_...\n");
327
- process.stderr.write("Create an API key at https://app.superx.so/account?tab=developers\n");
358
+ process.stderr.write("Create an API key at https://app.superx.so/account?tab=api\n");
328
359
  process.exit(1);
329
360
  }
330
361
 
@@ -347,7 +378,7 @@ async function login(argv) {
347
378
  let key = (argv.key || "").trim();
348
379
  if (!key) {
349
380
  note("Create or copy an API key at:");
350
- note(" https://app.superx.so/account?tab=developers");
381
+ note(" https://app.superx.so/account?tab=api");
351
382
  note("");
352
383
  key = await promptForKey();
353
384
  }
@@ -576,6 +607,26 @@ async function signalsDeleteAgent(argv) {
576
607
  printJson(await api.deleteSignalAgent(argv.id));
577
608
  }
578
609
 
610
+ // src/commands/engage.ts
611
+ async function engageFeeds(argv) {
612
+ const api = new SuperXAPI(getConfig());
613
+ printJson(await api.listEngageFeeds({ account_id: argv.account }));
614
+ }
615
+ async function engagePosts(argv) {
616
+ const api = new SuperXAPI(getConfig());
617
+ printJson(
618
+ await api.getEngageFeedPosts(argv.feedId, {
619
+ account_id: argv.account,
620
+ limit: argv.limit,
621
+ mode: argv.mode,
622
+ // yargs boolean: pass through only when the flag was given.
623
+ fresh: argv.fresh === void 0 ? void 0 : String(argv.fresh),
624
+ include_replied: argv.includeReplied === void 0 ? void 0 : String(argv.includeReplied),
625
+ exclude_post_ids: argv.exclude
626
+ })
627
+ );
628
+ }
629
+
579
630
  // src/commands/scheduled.ts
580
631
  async function scheduledList(argv) {
581
632
  const api = new SuperXAPI(getConfig());
@@ -628,6 +679,80 @@ function parsePartsJson(raw) {
628
679
  }
629
680
  return parsed;
630
681
  }
682
+ function numericFlag(name, value) {
683
+ if (value === void 0 || value === false) return value;
684
+ if (value === true) {
685
+ note(`--${name} needs a numeric value.`);
686
+ process.exit(1);
687
+ }
688
+ const n = typeof value === "number" ? value : Number(value);
689
+ if (!Number.isFinite(n)) {
690
+ note(`--${name} must be a number.`);
691
+ process.exit(1);
692
+ }
693
+ return n;
694
+ }
695
+ function applyAdvancedFlags(argv, body) {
696
+ const retweet = numericFlag("auto-retweet", argv["auto-retweet"]);
697
+ const retweetRemove = numericFlag("auto-retweet-remove", argv["auto-retweet-remove"]);
698
+ if (retweet === false) {
699
+ if (typeof retweetRemove === "number") {
700
+ note("--auto-retweet-remove cannot be combined with --no-auto-retweet.");
701
+ process.exit(1);
702
+ }
703
+ body.auto_retweet = null;
704
+ } else if (typeof retweet === "number") {
705
+ body.auto_retweet = {
706
+ after_hours: retweet,
707
+ ...typeof retweetRemove === "number" ? { remove_after_hours: retweetRemove } : {}
708
+ };
709
+ } else if (typeof retweetRemove === "number") {
710
+ note("--auto-retweet-remove requires --auto-retweet <hours>.");
711
+ process.exit(1);
712
+ }
713
+ const del = numericFlag("auto-delete", argv["auto-delete"]);
714
+ const delThreshold = numericFlag("auto-delete-threshold", argv["auto-delete-threshold"]);
715
+ if (del === false) {
716
+ if (typeof delThreshold === "number") {
717
+ note("--auto-delete-threshold cannot be combined with --no-auto-delete.");
718
+ process.exit(1);
719
+ }
720
+ body.auto_delete = null;
721
+ } else if (typeof del === "number") {
722
+ body.auto_delete = {
723
+ after_hours: del,
724
+ ...typeof delThreshold === "number" ? { threshold: delThreshold } : {}
725
+ };
726
+ } else if (typeof delThreshold === "number") {
727
+ note("--auto-delete-threshold requires --auto-delete <hours>.");
728
+ process.exit(1);
729
+ }
730
+ const plug = argv["auto-plug"];
731
+ const plugThreshold = numericFlag("auto-plug-threshold", argv["auto-plug-threshold"]);
732
+ if (plug === false) {
733
+ if (typeof plugThreshold === "number") {
734
+ note("--auto-plug-threshold cannot be combined with --no-auto-plug.");
735
+ process.exit(1);
736
+ }
737
+ body.auto_plug = null;
738
+ } else if (typeof plug === "string" && plug.length > 0) {
739
+ if (typeof plugThreshold !== "number") {
740
+ note("--auto-plug requires --auto-plug-threshold <likes>.");
741
+ process.exit(1);
742
+ }
743
+ body.auto_plug = { template_id: plug, threshold: plugThreshold };
744
+ } else if (typeof plugThreshold === "number") {
745
+ note("--auto-plug-threshold requires --auto-plug <templateId>.");
746
+ process.exit(1);
747
+ }
748
+ if (typeof argv["super-followers"] === "boolean") {
749
+ body.super_followers_only = argv["super-followers"];
750
+ }
751
+ }
752
+ async function plugTemplatesList(argv) {
753
+ const api = new SuperXAPI(getConfig());
754
+ printJson(await api.listPlugTemplates({ account_id: argv.account }));
755
+ }
631
756
  async function scheduledCreate(argv) {
632
757
  const parts = (argv.part || []).filter((p) => typeof p === "string");
633
758
  const sourceCount = [argv.text, parts.length > 0 ? "p" : void 0, argv["parts-json"]].filter(
@@ -663,6 +788,7 @@ async function scheduledCreate(argv) {
663
788
  if (argv.scratchpad !== void 0) body.scratchpad = argv.scratchpad;
664
789
  const tags = (argv.tag || []).filter((t) => typeof t === "string" && t.length > 0);
665
790
  if (tags.length > 0) body.tags = tags;
791
+ applyAdvancedFlags(argv, body);
666
792
  if (argv.account) body.account_id = argv.account;
667
793
  const api = new SuperXAPI(getConfig());
668
794
  const { json, replayed } = await api.createScheduled(body, argv["idempotency-key"]);
@@ -720,6 +846,7 @@ async function scheduledUpdate(argv) {
720
846
  if (argv["clear-scratchpad"]) body.scratchpad = null;
721
847
  if (tags.length > 0) body.tags = tags;
722
848
  if (argv["clear-tags"]) body.tags = [];
849
+ applyAdvancedFlags(argv, body);
723
850
  if (argv.account) body.account_id = argv.account;
724
851
  if (Object.keys(body).filter((k) => k !== "account_id").length === 0) {
725
852
  note("Provide at least one field to update. Run: superx scheduled:update --help");
@@ -941,6 +1068,140 @@ async function articlesCover(argv) {
941
1068
  printJson(await api.generateArticleCover(argv.id, body));
942
1069
  }
943
1070
 
1071
+ // src/commands/context.ts
1072
+ async function contextGet(argv) {
1073
+ const api = new SuperXAPI(getConfig());
1074
+ printJson(await api.getContext({ account_id: argv.account }));
1075
+ }
1076
+ function commaList(value) {
1077
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
1078
+ }
1079
+ function stringOrClear(value) {
1080
+ return value === "" ? null : value;
1081
+ }
1082
+ async function contextSet(argv) {
1083
+ const body = {};
1084
+ const profileDescription = {};
1085
+ if (argv["profile-description"] !== void 0) {
1086
+ profileDescription.text = stringOrClear(argv["profile-description"]);
1087
+ }
1088
+ if (typeof argv["profile-description-enabled"] === "boolean") {
1089
+ profileDescription.enabled = argv["profile-description-enabled"];
1090
+ }
1091
+ if (Object.keys(profileDescription).length > 0) {
1092
+ body.profile_description = profileDescription;
1093
+ }
1094
+ if (argv.interests !== void 0) {
1095
+ body.interests = commaList(argv.interests);
1096
+ }
1097
+ if (argv.rules !== void 0) {
1098
+ body.rules = stringOrClear(argv.rules);
1099
+ }
1100
+ const reply = {};
1101
+ if (argv["reply-rules"] !== void 0) {
1102
+ reply.custom_instructions = stringOrClear(argv["reply-rules"]);
1103
+ }
1104
+ if (typeof argv["reply-author-name"] === "boolean") {
1105
+ reply.include_author_name = argv["reply-author-name"];
1106
+ }
1107
+ if (Object.keys(reply).length > 0) {
1108
+ body.reply = reply;
1109
+ }
1110
+ const voice = {};
1111
+ if (argv["favorite-creators"] !== void 0) {
1112
+ voice.favorite_creators = commaList(argv["favorite-creators"]);
1113
+ }
1114
+ if (typeof argv["own-posts-as-examples"] === "boolean") {
1115
+ voice.use_own_posts_as_examples = argv["own-posts-as-examples"];
1116
+ }
1117
+ if (Object.keys(voice).length > 0) {
1118
+ body.voice = voice;
1119
+ }
1120
+ const styleGuide = {};
1121
+ if (argv["style-audience"] !== void 0) {
1122
+ styleGuide.audience_override = stringOrClear(argv["style-audience"]);
1123
+ }
1124
+ if (argv["style-vocabulary"] !== void 0) {
1125
+ styleGuide.vocabulary_override = stringOrClear(argv["style-vocabulary"]);
1126
+ }
1127
+ if (Object.keys(styleGuide).length > 0) {
1128
+ body.style_guide = styleGuide;
1129
+ }
1130
+ if (Object.keys(body).length === 0) {
1131
+ note("Provide at least one setting flag. Run: superx context:set --help");
1132
+ process.exit(1);
1133
+ }
1134
+ if (argv.account) body.account_id = argv.account;
1135
+ const api = new SuperXAPI(getConfig());
1136
+ printJson(await api.updateContext(body));
1137
+ }
1138
+ async function contextProducts(argv) {
1139
+ const api = new SuperXAPI(getConfig());
1140
+ const json = await api.getContext({ account_id: argv.account });
1141
+ printJson({ data: json?.data?.products ?? [] });
1142
+ }
1143
+ async function contextProductsSet(argv) {
1144
+ if (!argv.id && !argv.url) {
1145
+ note("Provide --id (from context:products) to edit, or --url to add or edit by url.");
1146
+ process.exit(1);
1147
+ }
1148
+ if (argv.id && argv.url) {
1149
+ note("Use either --id or --url, not both.");
1150
+ process.exit(1);
1151
+ }
1152
+ const body = {};
1153
+ if (argv.name !== void 0) body.name = stringOrClear(argv.name);
1154
+ if (argv.description !== void 0) body.description = stringOrClear(argv.description);
1155
+ if (argv.positioning !== void 0) body.positioning = stringOrClear(argv.positioning);
1156
+ if (argv.features !== void 0) body.features = stringOrClear(argv.features);
1157
+ if (argv.updates !== void 0) body.updates = stringOrClear(argv.updates);
1158
+ if (argv.account) body.account_id = argv.account;
1159
+ const api = new SuperXAPI(getConfig());
1160
+ printJson(await api.updateContextProduct(argv.id || argv.url, body));
1161
+ }
1162
+ async function contextProductsDelete(argv) {
1163
+ const id = String(argv.id || "").trim();
1164
+ if (!id) {
1165
+ note("Provide the product id. Run: superx context:products:delete --help");
1166
+ process.exit(1);
1167
+ }
1168
+ const api = new SuperXAPI(getConfig());
1169
+ printJson(await api.deleteContextProduct(id, { account_id: argv.account }));
1170
+ }
1171
+
1172
+ // src/commands/queue.ts
1173
+ async function queueGet(argv) {
1174
+ const api = new SuperXAPI(getConfig());
1175
+ printJson(await api.getQueueSettings({ account_id: argv.account }));
1176
+ }
1177
+ async function queueSet(argv) {
1178
+ const body = {};
1179
+ if (argv["slots-json"] !== void 0) {
1180
+ let parsed;
1181
+ try {
1182
+ parsed = JSON.parse(argv["slots-json"]);
1183
+ } catch {
1184
+ note(`--slots-json must be valid JSON, e.g. '[{"time":"09:00","days":[1,3,5]}]'`);
1185
+ process.exit(1);
1186
+ }
1187
+ if (!Array.isArray(parsed)) {
1188
+ note(`--slots-json must be a JSON array, e.g. '[{"time":"09:00","days":[1,3,5]}]' (use '[]' to clear).`);
1189
+ process.exit(1);
1190
+ }
1191
+ body.slots = parsed;
1192
+ }
1193
+ if (argv.timezone !== void 0) {
1194
+ body.timezone = argv.timezone;
1195
+ }
1196
+ if (Object.keys(body).length === 0) {
1197
+ note("Provide --slots-json and/or --timezone. Run: superx queue:set --help");
1198
+ process.exit(1);
1199
+ }
1200
+ if (argv.account) body.account_id = argv.account;
1201
+ const api = new SuperXAPI(getConfig());
1202
+ printJson(await api.updateQueueSettings(body));
1203
+ }
1204
+
944
1205
  // src/commands/docs.ts
945
1206
  async function docs() {
946
1207
  const apiUrl = resolveApiUrl(loadCredentials()?.apiUrl || DEFAULT_API_URL);
@@ -972,6 +1233,23 @@ var accountOption = (y) => y.option("account", {
972
1233
  describe: "Account id (from `superx accounts`); defaults to your main account",
973
1234
  type: "string"
974
1235
  });
1236
+ var advancedSettingsOptions = (y) => y.option("auto-retweet", {
1237
+ describe: "Auto retweet the post after this many hours (1-12); --no-auto-retweet turns it off"
1238
+ }).option("auto-retweet-remove", {
1239
+ describe: "Remove the auto retweet after this many hours (1-12); needs --auto-retweet"
1240
+ }).option("auto-delete", {
1241
+ describe: "Auto delete the post after this many hours (1-12) if it underperforms; --no-auto-delete turns it off"
1242
+ }).option("auto-delete-threshold", {
1243
+ describe: "Views threshold for --auto-delete: delete only below this many views (default 1000)"
1244
+ }).option("auto-plug", {
1245
+ describe: "Plug template id (from plug-templates:list) to auto-reply with; --no-auto-plug turns it off",
1246
+ type: "string"
1247
+ }).option("auto-plug-threshold", {
1248
+ describe: "Likes threshold for --auto-plug: the reply posts once the post hits this many likes"
1249
+ }).option("super-followers", {
1250
+ describe: "Post to Super Followers only (--no-super-followers turns it off)",
1251
+ type: "boolean"
1252
+ });
975
1253
  (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).scriptName("superx").usage("$0 <command> [options]").command(
976
1254
  "login",
977
1255
  "Authenticate with a SuperX API key (guided paste or --key)",
@@ -1121,6 +1399,32 @@ var accountOption = (y) => y.option("account", {
1121
1399
  "Delete a signal agent (its saved leads and contact list stay untouched)",
1122
1400
  (y) => y.positional("id", { describe: "Agent id (from signals:agents)", type: "number" }),
1123
1401
  run(signalsDeleteAgent)
1402
+ ).command(
1403
+ "engage:feeds",
1404
+ "List the Engage feeds set up in the app",
1405
+ (y) => accountOption(y).example("$0 engage:feeds", "Feed ids, types, and what one fetch costs").example("$0 engage:feeds --account <account-id>", "Feeds saved on a linked account"),
1406
+ run(engageFeeds)
1407
+ ).command(
1408
+ "engage:posts <feedId>",
1409
+ "Fetch the posts an Engage feed surfaces (for review; replies are sent by a person in the app)",
1410
+ (y) => accountOption(y).positional("feedId", { describe: "Feed id (from engage:feeds)", type: "string" }).option("limit", {
1411
+ describe: "Posts to return, 1-50 (default 20). Applies to keyword feeds; list feeds return one page (about 10 to 25 posts) and --limit only trims it",
1412
+ type: "number"
1413
+ }).option("mode", {
1414
+ describe: "top = highest-signal posts first (default); latest = newest first",
1415
+ type: "string",
1416
+ choices: ["top", "latest"]
1417
+ }).option("fresh", {
1418
+ describe: "true = skip the cache and fetch new posts; false = allow cached posts (default)",
1419
+ type: "boolean"
1420
+ }).option("include-replied", {
1421
+ describe: "true = keep posts already replied to, skipped, or blocked and flag them with `replied`; false = leave them out (default)",
1422
+ type: "boolean"
1423
+ }).option("exclude", {
1424
+ describe: "Comma list of post ids to leave out (max 100); this is how you page: pass the ids you already have to get the next batch",
1425
+ type: "string"
1426
+ }).example("$0 engage:posts <feed-id> --limit 50", "One big page from a keyword feed").example("$0 engage:posts <feed-id> --mode latest --fresh true", "Newest posts, skipping the cache").example("$0 engage:posts <feed-id> --exclude 1234567890,1234567891", "Next batch, minus the posts you have"),
1427
+ run(engagePosts)
1124
1428
  ).command(
1125
1429
  "scheduled:list",
1126
1430
  "List drafts and the scheduled queue",
@@ -1140,7 +1444,7 @@ var accountOption = (y) => y.option("account", {
1140
1444
  ).command(
1141
1445
  "scheduled:create",
1142
1446
  "Create a draft (no --at) or scheduled post; repeat --part for a thread",
1143
- (y) => accountOption(y).option("text", { describe: "Text for a single post", type: "string" }).option("part", {
1447
+ (y) => advancedSettingsOptions(accountOption(y)).option("text", { describe: "Text for a single post", type: "string" }).option("part", {
1144
1448
  describe: "Thread part text (repeat the flag, 1-25 parts, in order)",
1145
1449
  type: "string",
1146
1450
  array: true
@@ -1163,12 +1467,12 @@ var accountOption = (y) => y.option("account", {
1163
1467
  }).option("idempotency-key", {
1164
1468
  describe: "Idempotency-Key header (max 64 chars); retries with the same key return the original result",
1165
1469
  type: "string"
1166
- }).example('$0 scheduled:create --text "Hello"', "Create a draft").example('$0 scheduled:create --text "Hello" --at "2026-08-01T15:00:00Z"', "Schedule a post").example('$0 scheduled:create --part "1/ Hook" --part "2/ Detail" --part "3/ CTA"', "Draft a 3-part thread").example('$0 scheduled:create --text "Hello" --title "Launch teaser" --tag abc123', "Draft with a title and a tag").example('$0 scheduled:create --text "Chart of the week" --media "<object_key>" --alt-text "Revenue chart"', "Draft with an image"),
1470
+ }).example('$0 scheduled:create --text "Hello"', "Create a draft").example('$0 scheduled:create --text "Hello" --at "2026-08-01T15:00:00Z"', "Schedule a post").example('$0 scheduled:create --part "1/ Hook" --part "2/ Detail" --part "3/ CTA"', "Draft a 3-part thread").example('$0 scheduled:create --text "Hello" --title "Launch teaser" --tag abc123', "Draft with a title and a tag").example('$0 scheduled:create --text "Chart of the week" --media "<object_key>" --alt-text "Revenue chart"', "Draft with an image").example('$0 scheduled:create --text "Hello" --at "2026-08-01T15:00:00Z" --auto-retweet 6 --auto-retweet-remove 4', "Schedule with an auto retweet").example('$0 scheduled:create --text "Hello" --at "2026-08-01T15:00:00Z" --no-auto-retweet --no-auto-plug', "Schedule with your defaults off for this post"),
1167
1471
  run(scheduledCreate)
1168
1472
  ).command(
1169
1473
  "scheduled:update <id>",
1170
1474
  "Edit a draft or scheduled post; only the flags you pass change",
1171
- (y) => accountOption(y).positional("id", { describe: "Post id (from scheduled:list or scheduled:create)", type: "string" }).option("text", { describe: "Replacement text for a single post", type: "string" }).option("part", {
1475
+ (y) => advancedSettingsOptions(accountOption(y)).positional("id", { describe: "Post id (from scheduled:list or scheduled:create)", type: "string" }).option("text", { describe: "Replacement text for a single post", type: "string" }).option("part", {
1172
1476
  describe: "Replacement thread part text (repeat the flag, 1-25 parts, in order)",
1173
1477
  type: "string",
1174
1478
  array: true
@@ -1192,13 +1496,94 @@ var accountOption = (y) => y.option("account", {
1192
1496
  describe: "Replacement tag id set (repeat the flag, max 20; replaces ALL current tags)",
1193
1497
  type: "string",
1194
1498
  array: true
1195
- }).option("clear-tags", { describe: "Remove all tags", type: "boolean" }).example('$0 scheduled:update abc123 --title "Better hook"', "Retitle a draft, everything else untouched").example('$0 scheduled:update abc123 --at "2026-08-01T15:00:00Z" --status scheduled', "Promote a draft to the queue").example("$0 scheduled:update abc123 --status draft", "Pull a post back to drafts (quota refunds)"),
1499
+ }).option("clear-tags", { describe: "Remove all tags", type: "boolean" }).example('$0 scheduled:update abc123 --title "Better hook"', "Retitle a draft, everything else untouched").example('$0 scheduled:update abc123 --at "2026-08-01T15:00:00Z" --status scheduled', "Promote a draft to the queue").example("$0 scheduled:update abc123 --status draft", "Pull a post back to drafts (quota refunds)").example("$0 scheduled:update abc123 --auto-delete 8 --auto-delete-threshold 500", "Add an auto delete to the post").example("$0 scheduled:update abc123 --no-auto-retweet", "Remove the post's auto retweet"),
1196
1500
  run(scheduledUpdate)
1197
1501
  ).command(
1198
1502
  "scheduled:delete <id>",
1199
1503
  "Delete a draft or scheduled post by id",
1200
1504
  (y) => y.positional("id", { describe: "Post id (from scheduled:list or scheduled:create)", type: "string" }),
1201
1505
  run(scheduledDelete)
1506
+ ).command(
1507
+ "plug-templates:list",
1508
+ "List your auto-plug reply templates (id, text, has_media) for --auto-plug",
1509
+ (y) => accountOption(y),
1510
+ run(plugTemplatesList)
1511
+ ).command(
1512
+ "context:get",
1513
+ "Show the account's Context settings: profile description, interests, rules, reply settings, favorite creators, style guide, products",
1514
+ (y) => accountOption(y),
1515
+ run(contextGet)
1516
+ ).command(
1517
+ "context:set",
1518
+ 'Edit Context settings; only the flags you pass change (string flags: "" clears; lists fully replace)',
1519
+ (y) => accountOption(y).option("profile-description", {
1520
+ describe: 'Who you are and what you do, grounds the AI voice (max 500 chars; "" clears)',
1521
+ type: "string"
1522
+ }).option("profile-description-enabled", {
1523
+ describe: "Use the profile description in AI writing (--no-profile-description-enabled turns it off)",
1524
+ type: "boolean"
1525
+ }).option("interests", {
1526
+ describe: 'Comma list of topics you want content about (max 30, each max 50 chars; replaces the stored list; "" clears)',
1527
+ type: "string"
1528
+ }).option("rules", {
1529
+ describe: 'SuperX rules the AI must follow on every surface (max 500 chars; "" clears)',
1530
+ type: "string"
1531
+ }).option("reply-rules", {
1532
+ describe: 'Custom instructions for AI-generated replies (max 500 chars; "" clears)',
1533
+ type: "string"
1534
+ }).option("reply-author-name", {
1535
+ describe: "Let AI replies address the post author by name (--no-reply-author-name turns it off)",
1536
+ type: "boolean"
1537
+ }).option("favorite-creators", {
1538
+ describe: 'Comma list of X usernames whose style inspires yours (max 3; replaces the stored list; "" clears)',
1539
+ type: "string"
1540
+ }).option("own-posts-as-examples", {
1541
+ describe: "Use your own posts as voice examples (--no-own-posts-as-examples turns it off)",
1542
+ type: "boolean"
1543
+ }).option("style-audience", {
1544
+ describe: 'Manual audience description that outranks the generated style guide (max 600 chars; "" reverts to generated)',
1545
+ type: "string"
1546
+ }).option("style-vocabulary", {
1547
+ describe: 'Manual vocabulary and style description that outranks the generated style guide (max 1000 chars; "" reverts to generated)',
1548
+ type: "string"
1549
+ }).example('$0 context:set --rules "Never use hashtags. Keep posts under 200 chars."', "Set your SuperX rules").example('$0 context:set --interests "indie hacking,SaaS,AI agents"', "Replace your interests").example('$0 context:set --favorite-creators "levelsio,marc_louvion"', "Set favorite creators").example('$0 context:set --style-audience ""', "Revert to the generated audience description"),
1550
+ run(contextSet)
1551
+ ).command(
1552
+ "context:products",
1553
+ "List the account's products (used for product mentions in generated content)",
1554
+ (y) => accountOption(y),
1555
+ run(contextProducts)
1556
+ ).command(
1557
+ "context:products:set",
1558
+ "Add or edit ONE product by --url (creates it when new, cap 5) or --id",
1559
+ (y) => accountOption(y).option("id", { describe: "Product id (from context:products) to edit", type: "string" }).option("url", { describe: "Product http(s) url; creates the product when it does not exist", type: "string" }).option("name", { describe: 'Product name (max 200 chars; "" clears)', type: "string" }).option("description", { describe: 'What the product is (max 1000 chars; "" clears)', type: "string" }).option("positioning", { describe: 'Positioning and differentiation (max 2000 chars; "" clears)', type: "string" }).option("features", { describe: 'Key features (max 2000 chars; "" clears)', type: "string" }).option("updates", { describe: 'Recent updates worth mentioning (max 2000 chars; "" clears)', type: "string" }).example('$0 context:products:set --url "https://superx.so" --name "SuperX" --description "X growth platform"', "Add or edit a product by url").example('$0 context:products:set --id 3 --updates "Shipped the public API"', "Update one field by id"),
1560
+ run(contextProductsSet)
1561
+ ).command(
1562
+ "context:products:delete <id>",
1563
+ "Remove a product by id (reversible by re-adding the same url)",
1564
+ (y) => accountOption(y).positional("id", { describe: "Product id (from context:products)", type: "string" }),
1565
+ run(contextProductsDelete)
1566
+ ).command(
1567
+ "queue:get",
1568
+ "Show the account's posting schedule: predefined time slots and the timezone they run in",
1569
+ (y) => accountOption(y),
1570
+ run(queueGet)
1571
+ ).command(
1572
+ "queue:set",
1573
+ "Change the posting schedule; changing the slots also re-flows queued posts onto them",
1574
+ (y) => accountOption(y).option("slots-json", {
1575
+ describe: `JSON array of slots, e.g. '[{"time":"09:00","days":[1,3,5]}]' (0 = Sunday; max 50, one entry per time; replaces the stored slots; '[]' clears them)`,
1576
+ type: "string"
1577
+ }).option("timezone", {
1578
+ describe: 'IANA timezone the slot times run in, e.g. "Europe/London" (changing only this never moves queued posts)',
1579
+ type: "string"
1580
+ }).example(
1581
+ `$0 queue:set --slots-json '[{"time":"09:00","days":[1,2,3,4,5]},{"time":"17:30","days":[1,3,5]}]'`,
1582
+ "Replace the posting slots and re-flow the queue"
1583
+ ).example('$0 queue:set --timezone "Europe/London"', "Change the posting timezone without moving posts").example(`$0 queue:set --slots-json '[]'`, "Clear every predefined slot").epilogue(
1584
+ "Changing the timezone and the slots in one call usually moves nothing, because the existing posts were placed under the old timezone; to re-flow them, change the timezone first, then send the slots in a second call."
1585
+ ),
1586
+ run(queueSet)
1202
1587
  ).command("tags:list", "List your tags (id, name, color)", {}, run(tagsList)).command(
1203
1588
  "tags:create <name>",
1204
1589
  "Create a tag",
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "superx-cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "SuperX CLI - command line interface to the SuperX API for Twitter/X growth: read posts and analytics, find engaged contacts, and schedule posts and threads",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
- "superx": "./dist/index.js"
7
+ "superx": "dist/index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "dev": "tsup --watch",
@@ -36,7 +36,7 @@
36
36
  "license": "MIT",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/superx-so/superx-agent.git"
39
+ "url": "git+https://github.com/superx-so/superx-agent.git"
40
40
  },
41
41
  "homepage": "https://docs.superx.so",
42
42
  "bugs": {