superx-cli 0.1.0 → 0.2.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 +5 -1
- package/PLAYBOOK.md +3 -2
- package/README.md +82 -4
- package/SKILL.md +102 -5
- package/dist/index.js +338 -6
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.2.0 (2026-08-01)
|
|
4
4
|
|
|
5
|
+
- 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
6
|
- 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
7
|
- 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>`
|
|
8
|
+
- 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>`
|
|
9
|
+
- 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
|
|
10
|
+
- 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
11
|
|
|
8
12
|
## 0.1.0 (2026-07-06)
|
|
9
13
|
|
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
|
|
|
@@ -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),
|
|
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=
|
|
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. Omitting it means the main account.
|
|
84
84
|
|
|
85
85
|
### Posts
|
|
86
86
|
|
|
@@ -220,6 +220,22 @@ A new `--at` time alone never schedules a draft; pass `--status scheduled` expli
|
|
|
220
220
|
|
|
221
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.
|
|
222
222
|
|
|
223
|
+
### Advanced settings
|
|
224
|
+
|
|
225
|
+
Auto retweet, auto delete, auto plug, and Super Followers only are available as flags on `scheduled:create` and `scheduled:update`.
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --auto-retweet 6 --auto-retweet-remove 4
|
|
229
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --auto-delete 8 --auto-delete-threshold 500
|
|
230
|
+
superx plug-templates:list # template ids for --auto-plug
|
|
231
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --auto-plug <template-id> --auto-plug-threshold 50
|
|
232
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" --no-auto-retweet --no-auto-plug
|
|
233
|
+
superx scheduled:update <post-id> --auto-retweet 2 # override on an existing post
|
|
234
|
+
superx scheduled:update <post-id> --no-auto-delete # remove from an existing post
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
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.
|
|
238
|
+
|
|
223
239
|
### Tags
|
|
224
240
|
|
|
225
241
|
```bash
|
|
@@ -231,6 +247,46 @@ superx tags:delete <tag-id> # also removes it from every po
|
|
|
231
247
|
|
|
232
248
|
Tag names are unique (409 `duplicate_name` on collision) and capped at 40 characters.
|
|
233
249
|
|
|
250
|
+
### Context settings (AI writing background)
|
|
251
|
+
|
|
252
|
+
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.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
superx context:get # the whole context document
|
|
256
|
+
|
|
257
|
+
# Only the flags you pass change; "" clears a string; lists fully replace
|
|
258
|
+
superx context:set --rules "Never use hashtags. Keep posts under 200 chars."
|
|
259
|
+
superx context:set --profile-description "Indie hacker building SuperX" --profile-description-enabled
|
|
260
|
+
superx context:set --interests "indie hacking,SaaS,AI agents" # replaces the list
|
|
261
|
+
superx context:set --favorite-creators "levelsio,marc_louvion" # max 3, replaces the list
|
|
262
|
+
superx context:set --reply-rules "Be helpful, never salesy" --no-reply-author-name
|
|
263
|
+
superx context:set --style-audience "Bootstrapped SaaS founders" # outranks the generated guide
|
|
264
|
+
superx context:set --style-audience "" # revert to the generated guide
|
|
265
|
+
|
|
266
|
+
# Products (max 5): mentioned naturally in generated content
|
|
267
|
+
superx context:products
|
|
268
|
+
superx context:products:set --url "https://superx.so" --name "SuperX" --description "X growth platform"
|
|
269
|
+
superx context:products:set --id 3 --updates "Shipped the public API"
|
|
270
|
+
superx context:products:delete <product-id>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
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.
|
|
274
|
+
|
|
275
|
+
### Queue settings (posting schedule)
|
|
276
|
+
|
|
277
|
+
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.
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
superx queue:get # slots, timezone, and the is_default flags
|
|
281
|
+
|
|
282
|
+
# Slots are JSON so weekday sets stay unambiguous; 0 = Sunday
|
|
283
|
+
superx queue:set --slots-json '[{"time":"09:00","days":[1,2,3,4,5]},{"time":"17:30","days":[1,3,5]}]'
|
|
284
|
+
superx queue:set --timezone "Europe/London" # never moves queued posts
|
|
285
|
+
superx queue:set --slots-json '[]' # clear every predefined slot
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
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.
|
|
289
|
+
|
|
234
290
|
### Articles (long-form X posts)
|
|
235
291
|
|
|
236
292
|
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`).
|
|
@@ -342,6 +398,12 @@ The CLI talks to these SuperX API endpoints (base `https://api.superx.so/v1`):
|
|
|
342
398
|
| `/articles/:id/schedule` | POST | `articles:schedule <id>` |
|
|
343
399
|
| `/articles/:id/unschedule` | POST | `articles:unschedule <id>` |
|
|
344
400
|
| `/articles/:id/cover` | POST | `articles:cover <id>` |
|
|
401
|
+
| `/context` | GET | `context:get`, `context:products` |
|
|
402
|
+
| `/context` | PATCH | `context:set` |
|
|
403
|
+
| `/context/products/:id` | PATCH | `context:products:set` |
|
|
404
|
+
| `/context/products/:id` | DELETE | `context:products:delete <id>` |
|
|
405
|
+
| `/queue-settings` | GET | `queue:get` |
|
|
406
|
+
| `/queue-settings` | PATCH | `queue:set` |
|
|
345
407
|
| `/docs` | GET | `docs` (no auth) |
|
|
346
408
|
|
|
347
409
|
Full API reference: [docs.superx.so](https://docs.superx.so)
|
|
@@ -367,7 +429,8 @@ Exit code `0` = success, `1` = error. Error codes come straight from the API:
|
|
|
367
429
|
|------|---------|
|
|
368
430
|
| `invalid_api_key` (401) | Bad or revoked key; run `superx login` again |
|
|
369
431
|
| `insufficient_scope` (403) | Read-only key used for a write |
|
|
370
|
-
| `writes_main_account_only` (403) | `scheduled:create` with a linked account |
|
|
432
|
+
| `writes_main_account_only` (403) | `scheduled:create` with a linked or shared account |
|
|
433
|
+
| `editor_restricted` (403) | `context:set` or `context:products:*` on a share with Editor permission |
|
|
371
434
|
| `subscription_required` (403) | SuperX subscription lapsed |
|
|
372
435
|
| `account_not_found` (404) | `--account` id is not one of your accounts |
|
|
373
436
|
| `list_not_found` (404) | List id is not one of your contact lists |
|
|
@@ -416,6 +479,8 @@ src/
|
|
|
416
479
|
├── media.ts # media:upload
|
|
417
480
|
├── scheduled.ts # scheduled:list / scheduled:create / scheduled:update / scheduled:delete
|
|
418
481
|
├── tags.ts # tags:list / tags:create / tags:update / tags:delete
|
|
482
|
+
├── context.ts # context:get / context:set / context:products / context:products:set / context:products:delete
|
|
483
|
+
├── queue.ts # queue:get / queue:set
|
|
419
484
|
├── articles.ts # articles:list/get/create/update/delete/publish/schedule/unschedule/cover
|
|
420
485
|
└── docs.ts # docs
|
|
421
486
|
```
|
|
@@ -478,6 +543,19 @@ superx tags:create "Launch week" --color amber
|
|
|
478
543
|
superx tags:update <id> --name "Launch"
|
|
479
544
|
superx tags:delete <id>
|
|
480
545
|
|
|
546
|
+
# Context settings (AI writing background)
|
|
547
|
+
superx context:get
|
|
548
|
+
superx context:set --rules "Never use hashtags."
|
|
549
|
+
superx context:set --interests "indie hacking,SaaS" # replaces the list
|
|
550
|
+
superx context:products
|
|
551
|
+
superx context:products:set --url "https://superx.so" --name "SuperX"
|
|
552
|
+
superx context:products:delete <id>
|
|
553
|
+
|
|
554
|
+
# Queue settings (posting schedule; 0 = Sunday)
|
|
555
|
+
superx queue:get
|
|
556
|
+
superx queue:set --slots-json '[{"time":"09:00","days":[1,3,5]}]' # replaces the slots
|
|
557
|
+
superx queue:set --timezone "Europe/London" # never moves posts
|
|
558
|
+
|
|
481
559
|
# Articles (markdown bodies; publish needs X Premium)
|
|
482
560
|
superx articles:create --title "My article" --file draft.md
|
|
483
561
|
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),
|
|
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), 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,14 +21,14 @@ 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),
|
|
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=
|
|
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
|
|
|
@@ -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
|
|
|
@@ -244,6 +244,37 @@ superx scheduled:update <post-id> --clear-tags --clear-title --clear-scratchpad
|
|
|
244
244
|
- 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
245
|
- 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
246
|
|
|
247
|
+
### Advanced settings (auto retweet, auto delete, auto plug, super followers)
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Explicit values on create
|
|
251
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
|
|
252
|
+
--auto-retweet 6 --auto-retweet-remove 4
|
|
253
|
+
|
|
254
|
+
# Auto plug: reply with a template once the post hits a likes threshold
|
|
255
|
+
superx plug-templates:list # id, text, has_media
|
|
256
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
|
|
257
|
+
--auto-plug <template-id> --auto-plug-threshold 50
|
|
258
|
+
|
|
259
|
+
# Auto delete underperformers (delete after 8h if under 500 views)
|
|
260
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
|
|
261
|
+
--auto-delete 8 --auto-delete-threshold 500
|
|
262
|
+
|
|
263
|
+
# Turn the user's defaults OFF for one post
|
|
264
|
+
superx scheduled:create --text "Post" --at "2026-08-01T15:00:00Z" \
|
|
265
|
+
--no-auto-retweet --no-auto-plug
|
|
266
|
+
|
|
267
|
+
# Edit or remove on an existing post (no inheritance on update)
|
|
268
|
+
superx scheduled:update <post-id> --auto-retweet 2
|
|
269
|
+
superx scheduled:update <post-id> --no-auto-delete
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
- 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.
|
|
273
|
+
- On `scheduled:update` there is no inheritance: passed flags override, omitted flags keep the post's current settings, `--no-*` removes them.
|
|
274
|
+
- 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.
|
|
275
|
+
- 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.
|
|
276
|
+
- `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.
|
|
277
|
+
|
|
247
278
|
### Tags
|
|
248
279
|
|
|
249
280
|
```bash
|
|
@@ -255,6 +286,56 @@ superx tags:delete <tag-id> # also removes it from every po
|
|
|
255
286
|
|
|
256
287
|
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
288
|
|
|
289
|
+
### Context settings (AI writing background)
|
|
290
|
+
|
|
291
|
+
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.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
superx context:get # The whole context document
|
|
295
|
+
superx context:get | jq '.data.rules' # One section
|
|
296
|
+
|
|
297
|
+
# Only the flags you pass change; "" clears a string; lists fully replace
|
|
298
|
+
superx context:set --rules "Never use hashtags. Keep posts under 200 chars."
|
|
299
|
+
superx context:set --profile-description "Indie hacker building SuperX" --profile-description-enabled
|
|
300
|
+
superx context:set --interests "indie hacking,SaaS,AI agents" # replaces the list
|
|
301
|
+
superx context:set --favorite-creators "levelsio,marc_louvion" # max 3, replaces the list
|
|
302
|
+
superx context:set --reply-rules "Be helpful, never salesy" --no-reply-author-name
|
|
303
|
+
superx context:set --style-audience "Bootstrapped SaaS founders" # outranks the generated guide
|
|
304
|
+
superx context:set --style-audience "" # revert to the generated guide
|
|
305
|
+
|
|
306
|
+
# Products (max 5): mentioned naturally in generated content
|
|
307
|
+
superx context:products
|
|
308
|
+
superx context:products:set --url "https://superx.so" --name "SuperX" --description "X growth platform"
|
|
309
|
+
superx context:products:set --id 3 --updates "Shipped the public API"
|
|
310
|
+
superx context:products:delete <product-id>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
- 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.
|
|
314
|
+
- `context:get` also returns the read-only generated style guide (`style_guide.generated`) so you can see what a cleared override falls back to.
|
|
315
|
+
- 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.
|
|
316
|
+
- `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.
|
|
317
|
+
- 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.
|
|
318
|
+
|
|
319
|
+
### Queue settings (posting schedule)
|
|
320
|
+
|
|
321
|
+
The posting schedule is the set of predefined time slots the queue fills, plus the timezone they run in.
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
superx queue:get # slots, timezone, is_default flags
|
|
325
|
+
|
|
326
|
+
# Slots are JSON so weekday sets stay unambiguous; 0 = Sunday
|
|
327
|
+
superx queue:set --slots-json '[{"time":"09:00","days":[1,2,3,4,5]},{"time":"17:30","days":[1,3,5]}]'
|
|
328
|
+
superx queue:set --timezone "Europe/London" # never moves queued posts
|
|
329
|
+
superx queue:set --slots-json '[]' # clear every predefined slot
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
- `--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.
|
|
333
|
+
- 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.
|
|
334
|
+
- `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.
|
|
335
|
+
- 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.
|
|
336
|
+
- `slots_are_default` / `timezone_is_default` mark values the account has never set; SuperX is using its own default.
|
|
337
|
+
- 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).
|
|
338
|
+
|
|
258
339
|
### Articles (long-form X posts)
|
|
259
340
|
|
|
260
341
|
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 +446,7 @@ superx scheduled:list --status scheduled
|
|
|
365
446
|
1. **Naive timestamps are rejected (400)**. Always include `Z` or an offset: `2026-08-01T15:00:00Z`, not `2026-08-01T15:00:00`.
|
|
366
447
|
2. **Schedule window**: `--at` must be at least 60 seconds in the future and within 18 months.
|
|
367
448
|
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
|
|
449
|
+
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
450
|
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
451
|
6. **Size caps**: max 25 thread parts, 25,000 characters total.
|
|
371
452
|
7. **Rate limited (429)**: `rate_limited` on stderr with a retry delay. Back off; do not hammer.
|
|
@@ -387,6 +468,9 @@ superx scheduled:list --status scheduled
|
|
|
387
468
|
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
469
|
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
470
|
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.
|
|
471
|
+
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.
|
|
472
|
+
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.
|
|
473
|
+
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.
|
|
390
474
|
|
|
391
475
|
---
|
|
392
476
|
|
|
@@ -459,6 +543,19 @@ superx articles:publish <id>
|
|
|
459
543
|
superx articles:cover <id> --style "minimal"
|
|
460
544
|
superx articles:delete <id>
|
|
461
545
|
|
|
546
|
+
# Context settings (AI writing background)
|
|
547
|
+
superx context:get
|
|
548
|
+
superx context:set --rules "Never use hashtags."
|
|
549
|
+
superx context:set --interests "indie hacking,SaaS" # replaces the list
|
|
550
|
+
superx context:products
|
|
551
|
+
superx context:products:set --url "https://superx.so" --name "SuperX"
|
|
552
|
+
superx context:products:delete <id>
|
|
553
|
+
|
|
554
|
+
# Queue settings (posting schedule; 0 = Sunday)
|
|
555
|
+
superx queue:get
|
|
556
|
+
superx queue:set --slots-json '[{"time":"09:00","days":[1,3,5]}]' # replaces the slots
|
|
557
|
+
superx queue:set --timezone "Europe/London" # never moves posts
|
|
558
|
+
|
|
462
559
|
# Docs and help
|
|
463
560
|
superx docs # API quickstart (markdown)
|
|
464
561
|
superx --help # All commands
|
package/dist/index.js
CHANGED
|
@@ -200,6 +200,30 @@ var SuperXAPI = class {
|
|
|
200
200
|
async deleteScheduled(id) {
|
|
201
201
|
return (await this.request(`/scheduled-posts/${encodeURIComponent(id)}`, { method: "DELETE" })).json;
|
|
202
202
|
}
|
|
203
|
+
// --- Plug templates ---
|
|
204
|
+
async listPlugTemplates(query = {}) {
|
|
205
|
+
return (await this.request("/plug-templates", { query })).json;
|
|
206
|
+
}
|
|
207
|
+
// --- Context settings ---
|
|
208
|
+
async getContext(query = {}) {
|
|
209
|
+
return (await this.request("/context", { query })).json;
|
|
210
|
+
}
|
|
211
|
+
async updateContext(body) {
|
|
212
|
+
return (await this.request("/context", { method: "PATCH", body })).json;
|
|
213
|
+
}
|
|
214
|
+
async updateContextProduct(idOrUrl, body) {
|
|
215
|
+
return (await this.request(`/context/products/${encodeURIComponent(idOrUrl)}`, { method: "PATCH", body })).json;
|
|
216
|
+
}
|
|
217
|
+
async deleteContextProduct(id, query = {}) {
|
|
218
|
+
return (await this.request(`/context/products/${encodeURIComponent(id)}`, { method: "DELETE", query })).json;
|
|
219
|
+
}
|
|
220
|
+
// --- Queue settings ---
|
|
221
|
+
async getQueueSettings(query = {}) {
|
|
222
|
+
return (await this.request("/queue-settings", { query })).json;
|
|
223
|
+
}
|
|
224
|
+
async updateQueueSettings(body) {
|
|
225
|
+
return (await this.request("/queue-settings", { method: "PATCH", body })).json;
|
|
226
|
+
}
|
|
203
227
|
// --- Media ---
|
|
204
228
|
async createMediaUpload(body) {
|
|
205
229
|
return (await this.request("/media", { method: "POST", body })).json;
|
|
@@ -324,7 +348,7 @@ function getConfig() {
|
|
|
324
348
|
process.stderr.write("Not authenticated. Either:\n");
|
|
325
349
|
process.stderr.write(" 1. Run: superx login\n");
|
|
326
350
|
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=
|
|
351
|
+
process.stderr.write("Create an API key at https://app.superx.so/account?tab=api\n");
|
|
328
352
|
process.exit(1);
|
|
329
353
|
}
|
|
330
354
|
|
|
@@ -347,7 +371,7 @@ async function login(argv) {
|
|
|
347
371
|
let key = (argv.key || "").trim();
|
|
348
372
|
if (!key) {
|
|
349
373
|
note("Create or copy an API key at:");
|
|
350
|
-
note(" https://app.superx.so/account?tab=
|
|
374
|
+
note(" https://app.superx.so/account?tab=api");
|
|
351
375
|
note("");
|
|
352
376
|
key = await promptForKey();
|
|
353
377
|
}
|
|
@@ -628,6 +652,80 @@ function parsePartsJson(raw) {
|
|
|
628
652
|
}
|
|
629
653
|
return parsed;
|
|
630
654
|
}
|
|
655
|
+
function numericFlag(name, value) {
|
|
656
|
+
if (value === void 0 || value === false) return value;
|
|
657
|
+
if (value === true) {
|
|
658
|
+
note(`--${name} needs a numeric value.`);
|
|
659
|
+
process.exit(1);
|
|
660
|
+
}
|
|
661
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
662
|
+
if (!Number.isFinite(n)) {
|
|
663
|
+
note(`--${name} must be a number.`);
|
|
664
|
+
process.exit(1);
|
|
665
|
+
}
|
|
666
|
+
return n;
|
|
667
|
+
}
|
|
668
|
+
function applyAdvancedFlags(argv, body) {
|
|
669
|
+
const retweet = numericFlag("auto-retweet", argv["auto-retweet"]);
|
|
670
|
+
const retweetRemove = numericFlag("auto-retweet-remove", argv["auto-retweet-remove"]);
|
|
671
|
+
if (retweet === false) {
|
|
672
|
+
if (typeof retweetRemove === "number") {
|
|
673
|
+
note("--auto-retweet-remove cannot be combined with --no-auto-retweet.");
|
|
674
|
+
process.exit(1);
|
|
675
|
+
}
|
|
676
|
+
body.auto_retweet = null;
|
|
677
|
+
} else if (typeof retweet === "number") {
|
|
678
|
+
body.auto_retweet = {
|
|
679
|
+
after_hours: retweet,
|
|
680
|
+
...typeof retweetRemove === "number" ? { remove_after_hours: retweetRemove } : {}
|
|
681
|
+
};
|
|
682
|
+
} else if (typeof retweetRemove === "number") {
|
|
683
|
+
note("--auto-retweet-remove requires --auto-retweet <hours>.");
|
|
684
|
+
process.exit(1);
|
|
685
|
+
}
|
|
686
|
+
const del = numericFlag("auto-delete", argv["auto-delete"]);
|
|
687
|
+
const delThreshold = numericFlag("auto-delete-threshold", argv["auto-delete-threshold"]);
|
|
688
|
+
if (del === false) {
|
|
689
|
+
if (typeof delThreshold === "number") {
|
|
690
|
+
note("--auto-delete-threshold cannot be combined with --no-auto-delete.");
|
|
691
|
+
process.exit(1);
|
|
692
|
+
}
|
|
693
|
+
body.auto_delete = null;
|
|
694
|
+
} else if (typeof del === "number") {
|
|
695
|
+
body.auto_delete = {
|
|
696
|
+
after_hours: del,
|
|
697
|
+
...typeof delThreshold === "number" ? { threshold: delThreshold } : {}
|
|
698
|
+
};
|
|
699
|
+
} else if (typeof delThreshold === "number") {
|
|
700
|
+
note("--auto-delete-threshold requires --auto-delete <hours>.");
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
const plug = argv["auto-plug"];
|
|
704
|
+
const plugThreshold = numericFlag("auto-plug-threshold", argv["auto-plug-threshold"]);
|
|
705
|
+
if (plug === false) {
|
|
706
|
+
if (typeof plugThreshold === "number") {
|
|
707
|
+
note("--auto-plug-threshold cannot be combined with --no-auto-plug.");
|
|
708
|
+
process.exit(1);
|
|
709
|
+
}
|
|
710
|
+
body.auto_plug = null;
|
|
711
|
+
} else if (typeof plug === "string" && plug.length > 0) {
|
|
712
|
+
if (typeof plugThreshold !== "number") {
|
|
713
|
+
note("--auto-plug requires --auto-plug-threshold <likes>.");
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
body.auto_plug = { template_id: plug, threshold: plugThreshold };
|
|
717
|
+
} else if (typeof plugThreshold === "number") {
|
|
718
|
+
note("--auto-plug-threshold requires --auto-plug <templateId>.");
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
if (typeof argv["super-followers"] === "boolean") {
|
|
722
|
+
body.super_followers_only = argv["super-followers"];
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
async function plugTemplatesList(argv) {
|
|
726
|
+
const api = new SuperXAPI(getConfig());
|
|
727
|
+
printJson(await api.listPlugTemplates({ account_id: argv.account }));
|
|
728
|
+
}
|
|
631
729
|
async function scheduledCreate(argv) {
|
|
632
730
|
const parts = (argv.part || []).filter((p) => typeof p === "string");
|
|
633
731
|
const sourceCount = [argv.text, parts.length > 0 ? "p" : void 0, argv["parts-json"]].filter(
|
|
@@ -663,6 +761,7 @@ async function scheduledCreate(argv) {
|
|
|
663
761
|
if (argv.scratchpad !== void 0) body.scratchpad = argv.scratchpad;
|
|
664
762
|
const tags = (argv.tag || []).filter((t) => typeof t === "string" && t.length > 0);
|
|
665
763
|
if (tags.length > 0) body.tags = tags;
|
|
764
|
+
applyAdvancedFlags(argv, body);
|
|
666
765
|
if (argv.account) body.account_id = argv.account;
|
|
667
766
|
const api = new SuperXAPI(getConfig());
|
|
668
767
|
const { json, replayed } = await api.createScheduled(body, argv["idempotency-key"]);
|
|
@@ -720,6 +819,7 @@ async function scheduledUpdate(argv) {
|
|
|
720
819
|
if (argv["clear-scratchpad"]) body.scratchpad = null;
|
|
721
820
|
if (tags.length > 0) body.tags = tags;
|
|
722
821
|
if (argv["clear-tags"]) body.tags = [];
|
|
822
|
+
applyAdvancedFlags(argv, body);
|
|
723
823
|
if (argv.account) body.account_id = argv.account;
|
|
724
824
|
if (Object.keys(body).filter((k) => k !== "account_id").length === 0) {
|
|
725
825
|
note("Provide at least one field to update. Run: superx scheduled:update --help");
|
|
@@ -941,6 +1041,140 @@ async function articlesCover(argv) {
|
|
|
941
1041
|
printJson(await api.generateArticleCover(argv.id, body));
|
|
942
1042
|
}
|
|
943
1043
|
|
|
1044
|
+
// src/commands/context.ts
|
|
1045
|
+
async function contextGet(argv) {
|
|
1046
|
+
const api = new SuperXAPI(getConfig());
|
|
1047
|
+
printJson(await api.getContext({ account_id: argv.account }));
|
|
1048
|
+
}
|
|
1049
|
+
function commaList(value) {
|
|
1050
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1051
|
+
}
|
|
1052
|
+
function stringOrClear(value) {
|
|
1053
|
+
return value === "" ? null : value;
|
|
1054
|
+
}
|
|
1055
|
+
async function contextSet(argv) {
|
|
1056
|
+
const body = {};
|
|
1057
|
+
const profileDescription = {};
|
|
1058
|
+
if (argv["profile-description"] !== void 0) {
|
|
1059
|
+
profileDescription.text = stringOrClear(argv["profile-description"]);
|
|
1060
|
+
}
|
|
1061
|
+
if (typeof argv["profile-description-enabled"] === "boolean") {
|
|
1062
|
+
profileDescription.enabled = argv["profile-description-enabled"];
|
|
1063
|
+
}
|
|
1064
|
+
if (Object.keys(profileDescription).length > 0) {
|
|
1065
|
+
body.profile_description = profileDescription;
|
|
1066
|
+
}
|
|
1067
|
+
if (argv.interests !== void 0) {
|
|
1068
|
+
body.interests = commaList(argv.interests);
|
|
1069
|
+
}
|
|
1070
|
+
if (argv.rules !== void 0) {
|
|
1071
|
+
body.rules = stringOrClear(argv.rules);
|
|
1072
|
+
}
|
|
1073
|
+
const reply = {};
|
|
1074
|
+
if (argv["reply-rules"] !== void 0) {
|
|
1075
|
+
reply.custom_instructions = stringOrClear(argv["reply-rules"]);
|
|
1076
|
+
}
|
|
1077
|
+
if (typeof argv["reply-author-name"] === "boolean") {
|
|
1078
|
+
reply.include_author_name = argv["reply-author-name"];
|
|
1079
|
+
}
|
|
1080
|
+
if (Object.keys(reply).length > 0) {
|
|
1081
|
+
body.reply = reply;
|
|
1082
|
+
}
|
|
1083
|
+
const voice = {};
|
|
1084
|
+
if (argv["favorite-creators"] !== void 0) {
|
|
1085
|
+
voice.favorite_creators = commaList(argv["favorite-creators"]);
|
|
1086
|
+
}
|
|
1087
|
+
if (typeof argv["own-posts-as-examples"] === "boolean") {
|
|
1088
|
+
voice.use_own_posts_as_examples = argv["own-posts-as-examples"];
|
|
1089
|
+
}
|
|
1090
|
+
if (Object.keys(voice).length > 0) {
|
|
1091
|
+
body.voice = voice;
|
|
1092
|
+
}
|
|
1093
|
+
const styleGuide = {};
|
|
1094
|
+
if (argv["style-audience"] !== void 0) {
|
|
1095
|
+
styleGuide.audience_override = stringOrClear(argv["style-audience"]);
|
|
1096
|
+
}
|
|
1097
|
+
if (argv["style-vocabulary"] !== void 0) {
|
|
1098
|
+
styleGuide.vocabulary_override = stringOrClear(argv["style-vocabulary"]);
|
|
1099
|
+
}
|
|
1100
|
+
if (Object.keys(styleGuide).length > 0) {
|
|
1101
|
+
body.style_guide = styleGuide;
|
|
1102
|
+
}
|
|
1103
|
+
if (Object.keys(body).length === 0) {
|
|
1104
|
+
note("Provide at least one setting flag. Run: superx context:set --help");
|
|
1105
|
+
process.exit(1);
|
|
1106
|
+
}
|
|
1107
|
+
if (argv.account) body.account_id = argv.account;
|
|
1108
|
+
const api = new SuperXAPI(getConfig());
|
|
1109
|
+
printJson(await api.updateContext(body));
|
|
1110
|
+
}
|
|
1111
|
+
async function contextProducts(argv) {
|
|
1112
|
+
const api = new SuperXAPI(getConfig());
|
|
1113
|
+
const json = await api.getContext({ account_id: argv.account });
|
|
1114
|
+
printJson({ data: json?.data?.products ?? [] });
|
|
1115
|
+
}
|
|
1116
|
+
async function contextProductsSet(argv) {
|
|
1117
|
+
if (!argv.id && !argv.url) {
|
|
1118
|
+
note("Provide --id (from context:products) to edit, or --url to add or edit by url.");
|
|
1119
|
+
process.exit(1);
|
|
1120
|
+
}
|
|
1121
|
+
if (argv.id && argv.url) {
|
|
1122
|
+
note("Use either --id or --url, not both.");
|
|
1123
|
+
process.exit(1);
|
|
1124
|
+
}
|
|
1125
|
+
const body = {};
|
|
1126
|
+
if (argv.name !== void 0) body.name = stringOrClear(argv.name);
|
|
1127
|
+
if (argv.description !== void 0) body.description = stringOrClear(argv.description);
|
|
1128
|
+
if (argv.positioning !== void 0) body.positioning = stringOrClear(argv.positioning);
|
|
1129
|
+
if (argv.features !== void 0) body.features = stringOrClear(argv.features);
|
|
1130
|
+
if (argv.updates !== void 0) body.updates = stringOrClear(argv.updates);
|
|
1131
|
+
if (argv.account) body.account_id = argv.account;
|
|
1132
|
+
const api = new SuperXAPI(getConfig());
|
|
1133
|
+
printJson(await api.updateContextProduct(argv.id || argv.url, body));
|
|
1134
|
+
}
|
|
1135
|
+
async function contextProductsDelete(argv) {
|
|
1136
|
+
const id = String(argv.id || "").trim();
|
|
1137
|
+
if (!id) {
|
|
1138
|
+
note("Provide the product id. Run: superx context:products:delete --help");
|
|
1139
|
+
process.exit(1);
|
|
1140
|
+
}
|
|
1141
|
+
const api = new SuperXAPI(getConfig());
|
|
1142
|
+
printJson(await api.deleteContextProduct(id, { account_id: argv.account }));
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// src/commands/queue.ts
|
|
1146
|
+
async function queueGet(argv) {
|
|
1147
|
+
const api = new SuperXAPI(getConfig());
|
|
1148
|
+
printJson(await api.getQueueSettings({ account_id: argv.account }));
|
|
1149
|
+
}
|
|
1150
|
+
async function queueSet(argv) {
|
|
1151
|
+
const body = {};
|
|
1152
|
+
if (argv["slots-json"] !== void 0) {
|
|
1153
|
+
let parsed;
|
|
1154
|
+
try {
|
|
1155
|
+
parsed = JSON.parse(argv["slots-json"]);
|
|
1156
|
+
} catch {
|
|
1157
|
+
note(`--slots-json must be valid JSON, e.g. '[{"time":"09:00","days":[1,3,5]}]'`);
|
|
1158
|
+
process.exit(1);
|
|
1159
|
+
}
|
|
1160
|
+
if (!Array.isArray(parsed)) {
|
|
1161
|
+
note(`--slots-json must be a JSON array, e.g. '[{"time":"09:00","days":[1,3,5]}]' (use '[]' to clear).`);
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
}
|
|
1164
|
+
body.slots = parsed;
|
|
1165
|
+
}
|
|
1166
|
+
if (argv.timezone !== void 0) {
|
|
1167
|
+
body.timezone = argv.timezone;
|
|
1168
|
+
}
|
|
1169
|
+
if (Object.keys(body).length === 0) {
|
|
1170
|
+
note("Provide --slots-json and/or --timezone. Run: superx queue:set --help");
|
|
1171
|
+
process.exit(1);
|
|
1172
|
+
}
|
|
1173
|
+
if (argv.account) body.account_id = argv.account;
|
|
1174
|
+
const api = new SuperXAPI(getConfig());
|
|
1175
|
+
printJson(await api.updateQueueSettings(body));
|
|
1176
|
+
}
|
|
1177
|
+
|
|
944
1178
|
// src/commands/docs.ts
|
|
945
1179
|
async function docs() {
|
|
946
1180
|
const apiUrl = resolveApiUrl(loadCredentials()?.apiUrl || DEFAULT_API_URL);
|
|
@@ -972,6 +1206,23 @@ var accountOption = (y) => y.option("account", {
|
|
|
972
1206
|
describe: "Account id (from `superx accounts`); defaults to your main account",
|
|
973
1207
|
type: "string"
|
|
974
1208
|
});
|
|
1209
|
+
var advancedSettingsOptions = (y) => y.option("auto-retweet", {
|
|
1210
|
+
describe: "Auto retweet the post after this many hours (1-12); --no-auto-retweet turns it off"
|
|
1211
|
+
}).option("auto-retweet-remove", {
|
|
1212
|
+
describe: "Remove the auto retweet after this many hours (1-12); needs --auto-retweet"
|
|
1213
|
+
}).option("auto-delete", {
|
|
1214
|
+
describe: "Auto delete the post after this many hours (1-12) if it underperforms; --no-auto-delete turns it off"
|
|
1215
|
+
}).option("auto-delete-threshold", {
|
|
1216
|
+
describe: "Views threshold for --auto-delete: delete only below this many views (default 1000)"
|
|
1217
|
+
}).option("auto-plug", {
|
|
1218
|
+
describe: "Plug template id (from plug-templates:list) to auto-reply with; --no-auto-plug turns it off",
|
|
1219
|
+
type: "string"
|
|
1220
|
+
}).option("auto-plug-threshold", {
|
|
1221
|
+
describe: "Likes threshold for --auto-plug: the reply posts once the post hits this many likes"
|
|
1222
|
+
}).option("super-followers", {
|
|
1223
|
+
describe: "Post to Super Followers only (--no-super-followers turns it off)",
|
|
1224
|
+
type: "boolean"
|
|
1225
|
+
});
|
|
975
1226
|
(0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).scriptName("superx").usage("$0 <command> [options]").command(
|
|
976
1227
|
"login",
|
|
977
1228
|
"Authenticate with a SuperX API key (guided paste or --key)",
|
|
@@ -1140,7 +1391,7 @@ var accountOption = (y) => y.option("account", {
|
|
|
1140
1391
|
).command(
|
|
1141
1392
|
"scheduled:create",
|
|
1142
1393
|
"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", {
|
|
1394
|
+
(y) => advancedSettingsOptions(accountOption(y)).option("text", { describe: "Text for a single post", type: "string" }).option("part", {
|
|
1144
1395
|
describe: "Thread part text (repeat the flag, 1-25 parts, in order)",
|
|
1145
1396
|
type: "string",
|
|
1146
1397
|
array: true
|
|
@@ -1163,12 +1414,12 @@ var accountOption = (y) => y.option("account", {
|
|
|
1163
1414
|
}).option("idempotency-key", {
|
|
1164
1415
|
describe: "Idempotency-Key header (max 64 chars); retries with the same key return the original result",
|
|
1165
1416
|
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"),
|
|
1417
|
+
}).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
1418
|
run(scheduledCreate)
|
|
1168
1419
|
).command(
|
|
1169
1420
|
"scheduled:update <id>",
|
|
1170
1421
|
"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", {
|
|
1422
|
+
(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
1423
|
describe: "Replacement thread part text (repeat the flag, 1-25 parts, in order)",
|
|
1173
1424
|
type: "string",
|
|
1174
1425
|
array: true
|
|
@@ -1192,13 +1443,94 @@ var accountOption = (y) => y.option("account", {
|
|
|
1192
1443
|
describe: "Replacement tag id set (repeat the flag, max 20; replaces ALL current tags)",
|
|
1193
1444
|
type: "string",
|
|
1194
1445
|
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)"),
|
|
1446
|
+
}).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
1447
|
run(scheduledUpdate)
|
|
1197
1448
|
).command(
|
|
1198
1449
|
"scheduled:delete <id>",
|
|
1199
1450
|
"Delete a draft or scheduled post by id",
|
|
1200
1451
|
(y) => y.positional("id", { describe: "Post id (from scheduled:list or scheduled:create)", type: "string" }),
|
|
1201
1452
|
run(scheduledDelete)
|
|
1453
|
+
).command(
|
|
1454
|
+
"plug-templates:list",
|
|
1455
|
+
"List your auto-plug reply templates (id, text, has_media) for --auto-plug",
|
|
1456
|
+
(y) => accountOption(y),
|
|
1457
|
+
run(plugTemplatesList)
|
|
1458
|
+
).command(
|
|
1459
|
+
"context:get",
|
|
1460
|
+
"Show the account's Context settings: profile description, interests, rules, reply settings, favorite creators, style guide, products",
|
|
1461
|
+
(y) => accountOption(y),
|
|
1462
|
+
run(contextGet)
|
|
1463
|
+
).command(
|
|
1464
|
+
"context:set",
|
|
1465
|
+
'Edit Context settings; only the flags you pass change (string flags: "" clears; lists fully replace)',
|
|
1466
|
+
(y) => accountOption(y).option("profile-description", {
|
|
1467
|
+
describe: 'Who you are and what you do, grounds the AI voice (max 500 chars; "" clears)',
|
|
1468
|
+
type: "string"
|
|
1469
|
+
}).option("profile-description-enabled", {
|
|
1470
|
+
describe: "Use the profile description in AI writing (--no-profile-description-enabled turns it off)",
|
|
1471
|
+
type: "boolean"
|
|
1472
|
+
}).option("interests", {
|
|
1473
|
+
describe: 'Comma list of topics you want content about (max 30, each max 50 chars; replaces the stored list; "" clears)',
|
|
1474
|
+
type: "string"
|
|
1475
|
+
}).option("rules", {
|
|
1476
|
+
describe: 'SuperX rules the AI must follow on every surface (max 500 chars; "" clears)',
|
|
1477
|
+
type: "string"
|
|
1478
|
+
}).option("reply-rules", {
|
|
1479
|
+
describe: 'Custom instructions for AI-generated replies (max 500 chars; "" clears)',
|
|
1480
|
+
type: "string"
|
|
1481
|
+
}).option("reply-author-name", {
|
|
1482
|
+
describe: "Let AI replies address the post author by name (--no-reply-author-name turns it off)",
|
|
1483
|
+
type: "boolean"
|
|
1484
|
+
}).option("favorite-creators", {
|
|
1485
|
+
describe: 'Comma list of X usernames whose style inspires yours (max 3; replaces the stored list; "" clears)',
|
|
1486
|
+
type: "string"
|
|
1487
|
+
}).option("own-posts-as-examples", {
|
|
1488
|
+
describe: "Use your own posts as voice examples (--no-own-posts-as-examples turns it off)",
|
|
1489
|
+
type: "boolean"
|
|
1490
|
+
}).option("style-audience", {
|
|
1491
|
+
describe: 'Manual audience description that outranks the generated style guide (max 600 chars; "" reverts to generated)',
|
|
1492
|
+
type: "string"
|
|
1493
|
+
}).option("style-vocabulary", {
|
|
1494
|
+
describe: 'Manual vocabulary and style description that outranks the generated style guide (max 1000 chars; "" reverts to generated)',
|
|
1495
|
+
type: "string"
|
|
1496
|
+
}).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"),
|
|
1497
|
+
run(contextSet)
|
|
1498
|
+
).command(
|
|
1499
|
+
"context:products",
|
|
1500
|
+
"List the account's products (used for product mentions in generated content)",
|
|
1501
|
+
(y) => accountOption(y),
|
|
1502
|
+
run(contextProducts)
|
|
1503
|
+
).command(
|
|
1504
|
+
"context:products:set",
|
|
1505
|
+
"Add or edit ONE product by --url (creates it when new, cap 5) or --id",
|
|
1506
|
+
(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"),
|
|
1507
|
+
run(contextProductsSet)
|
|
1508
|
+
).command(
|
|
1509
|
+
"context:products:delete <id>",
|
|
1510
|
+
"Remove a product by id (reversible by re-adding the same url)",
|
|
1511
|
+
(y) => accountOption(y).positional("id", { describe: "Product id (from context:products)", type: "string" }),
|
|
1512
|
+
run(contextProductsDelete)
|
|
1513
|
+
).command(
|
|
1514
|
+
"queue:get",
|
|
1515
|
+
"Show the account's posting schedule: predefined time slots and the timezone they run in",
|
|
1516
|
+
(y) => accountOption(y),
|
|
1517
|
+
run(queueGet)
|
|
1518
|
+
).command(
|
|
1519
|
+
"queue:set",
|
|
1520
|
+
"Change the posting schedule; changing the slots also re-flows queued posts onto them",
|
|
1521
|
+
(y) => accountOption(y).option("slots-json", {
|
|
1522
|
+
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)`,
|
|
1523
|
+
type: "string"
|
|
1524
|
+
}).option("timezone", {
|
|
1525
|
+
describe: 'IANA timezone the slot times run in, e.g. "Europe/London" (changing only this never moves queued posts)',
|
|
1526
|
+
type: "string"
|
|
1527
|
+
}).example(
|
|
1528
|
+
`$0 queue:set --slots-json '[{"time":"09:00","days":[1,2,3,4,5]},{"time":"17:30","days":[1,3,5]}]'`,
|
|
1529
|
+
"Replace the posting slots and re-flow the queue"
|
|
1530
|
+
).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(
|
|
1531
|
+
"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."
|
|
1532
|
+
),
|
|
1533
|
+
run(queueSet)
|
|
1202
1534
|
).command("tags:list", "List your tags (id, name, color)", {}, run(tagsList)).command(
|
|
1203
1535
|
"tags:create <name>",
|
|
1204
1536
|
"Create a tag",
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superx-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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": "
|
|
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": {
|