community/x

X (Twitter) API client for posting, reading, and managing tweets. Authenticate with OAuth 2.0 (PKCE), post and delete tweets, read your timeline, search, and look up users — all from the command line.

Installation

aux4 aux4 pkger install community/x

Node.js is required (used by post, reply, and thread) and is installed automatically as a system dependency if missing.

Posting images/media is not supported yet.

Prerequisites

You need an X Developer account and an app with OAuth 2.0 enabled:

  1. Go to the X Developer Portal
  2. Create a project and app
  3. Open User authentication settings and set it up:
  • App permissions: Read and write (required to post)
  • Type of App: Native App — a public client, so no client secret is needed
  • Callback URI / Redirect URL: exactly http://localhost:9876/callback
  • Website URL: any valid URL
  1. Save, then copy the OAuth 2.0 Client ID from the "Keys and tokens" tab

The OAuth 2.0 Client ID is not the same as the Consumer Key (OAuth 1.0a) or the Bearer Token (app-only) — those cannot post on your behalf.

Posting also requires credits on your X API account; without them every call returns 402 credits depleted.

Quick Start

# Authenticate with X (opens browser for OAuth)
aux4 x login --clientId YOUR_CLIENT_ID

# Post a tweet
aux4 x post "Hello from aux4!"

# Post a whole thread from a file
aux4 x thread thread.md

# Read your timeline
aux4 x timeline

# Search tweets
aux4 x search "aux4 cli"

# Get your profile
aux4 x me

Environment Variables

You can set credentials as environment variables to avoid passing them on every login:

| Variable | Description | |----------|-------------| | X_CLIENT_ID | X API client ID | | X_CLIENT_SECRET | X API client secret | | X_API_URL | X API base URL (default: https://api.x.com) |

Commands

aux4 x login

Authenticate with X using OAuth 2.0 with PKCE. Opens a local callback server and prints a URL to authorize in the browser. After authorization, tokens are saved locally.

aux4 x login --clientId <id> [--clientSecret <secret>] [--scopes <scopes>] [--callbackPort <port>]

| Flag | Description | Default | |------|-------------|---------| | --clientId | OAuth 2.0 Client ID | required (or X_CLIENT_ID env) | | --clientSecret | Client secret — leave unset for a Native/public app | optional (or X_CLIENT_SECRET env) | | --scopes | Space-separated OAuth scopes | tweet.read tweet.write users.read offline.access | | --callbackPort | Local callback server port | 9876 |

Note: The redirect URL in your X app settings must match http://localhost:<port>/callback.

Read-only access

Drop tweet.write to grant a token that can read but never post, reply, or delete:

aux4 x login --clientId YOUR_CLIENT_ID --scopes "tweet.read users.read offline.access"

Keep offline.access so the token can refresh itself. Scopes are capped by your app's App permissions setting, and changing them requires logging in again.

aux4 x logout

Remove stored X credentials.

aux4 x logout

aux4 x status

Show X authentication status, including token expiry and scopes.

aux4 x status

aux4 x me

Get your X profile information.

aux4 x me

aux4 x post

Post a tweet. Pass the text directly — the request body is built for you.

aux4 x post <text>

| Flag | Description | Default | |------|-------------|---------| | text | The text of the tweet (positional argument) | required |

Example

aux4 x post "Hello world!"

aux4 x reply

Reply to a tweet. Give the target tweet ID and the reply text; the reply is wired to the tweet for you.

aux4 x reply <tweetId> --text <text>

| Flag | Description | Default | |------|-------------|---------| | tweetId | ID of the tweet to reply to (positional argument) | required | | --text | The text of the reply | required |

Example

aux4 x reply 1234567890 --text "Great post!"

aux4 x thread

Post a whole thread from a file. Each post is published in order and chained as a reply to the previous one.

aux4 x thread <file> [--dryRun <true|false>]

| Flag | Description | Default | |------|-------------|---------| | file | Path to the thread file (positional argument) | required | | --dryRun | Print each request body instead of posting | false |

The file is split into posts on lines beginning with --- (for example --- Post 1); trailing metadata such as [attach: banner.png] or (chars: 230) on that line is ignored. This matches the format produced by the aux4-thread writing workflow, so a generated thread file can be posted as-is. Image attachments are not supported yet and are skipped with a note. If a post fails, the command stops and reports how many posts were already published.

Examples

# Preview the whole thread before posting
aux4 x thread thread.md --dryRun true

# Post it
aux4 x thread thread.md

aux4 x read

Read a specific tweet by ID. Returns tweet text, author, creation date, and public metrics.

aux4 x read <tweetId>

aux4 x delete

Delete a tweet. Prompts for confirmation before deleting.

aux4 x delete <tweetId>

aux4 x timeline

Read your home timeline.

aux4 x timeline [--maxResults <n>]

| Flag | Description | Default | |------|-------------|---------| | --maxResults | Number of tweets to return (1-100) | 10 |

aux4 x search

Search recent tweets (up to 7 days).

aux4 x search <query> [--maxResults <n>]

| Flag | Description | Default | |------|-------------|---------| | query | Search query (positional argument) | required | | --maxResults | Number of results to return (10-100) | 10 |

Examples

# Simple search
aux4 x search "aux4 cli"

# Search with hashtag
aux4 x search "#opensource"

# Search from a user
aux4 x search "from:username"

aux4 x user

Look up a user by username.

aux4 x user <username>

Example

aux4 x user elonmusk

Token Storage

Tokens are stored in .oauth/x.json in the current directory. Add .oauth/ to your .gitignore. Tokens are automatically refreshed when expired.