sferik/x-cli
A command-line power tool for Twitter.
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 6d ago
- ✓2 active contributors
- ✓MIT licensed
Show 4 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Small team — 2 contributors active in recent commits
- ⚠Single-maintainer risk — top contributor 87% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/sferik/x-cli)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/sferik/x-cli on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: sferik/x-cli
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/sferik/x-cli shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 6d ago
- 2 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Small team — 2 contributors active in recent commits
- ⚠ Single-maintainer risk — top contributor 87% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live sferik/x-cli
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/sferik/x-cli.
What it runs against: a local clone of sferik/x-cli — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in sferik/x-cli | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 36 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of sferik/x-cli. If you don't
# have one yet, run these first:
#
# git clone https://github.com/sferik/x-cli.git
# cd x-cli
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of sferik/x-cli and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "sferik/x-cli(\\.git)?\\b" \\
&& ok "origin remote is sferik/x-cli" \\
|| miss "origin remote is not sferik/x-cli (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/lib.rs" \\
&& ok "src/lib.rs" \\
|| miss "missing critical file: src/lib.rs"
test -f "Cargo.toml" \\
&& ok "Cargo.toml" \\
|| miss "missing critical file: Cargo.toml"
test -f "x-api" \\
&& ok "x-api" \\
|| miss "missing critical file: x-api"
test -f ".github/workflows/test.yml" \\
&& ok ".github/workflows/test.yml" \\
|| miss "missing critical file: .github/workflows/test.yml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 36 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~6d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/sferik/x-cli"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
x-cli is a Rust-based command-line power tool for the X (Twitter) API that provides a full command tree with OAuth 1.0a/2.0 authentication, automatic retry logic with backoff, and support for both V1.1 and V2 API endpoints. It handles streaming, list management, search, and tweet deletion with column-aligned output formatting and YAML configuration via ~/.xrc profiles. Dual-language structure: the main Rust binary lives in src/ (main.rs, lib.rs) using clap for CLI parsing; the x-api local crate (referenced in Cargo.toml as { path = "x-api" }) handles HTTP, auth, and retry primitives; the /legacy directory contains the original Ruby implementation for reference/fallback; shell completion scripts (fish, bash, zsh) are in /etc.
👥Who it's for
Twitter/X power users and developers who need programmatic access to X APIs from the terminal; automation engineers building scripts around X; DevOps and data teams who want to stream tweets or manage accounts without building custom Python/JavaScript wrappers.
🌱Maturity & risk
Production-ready but in active transition: v6.0.0 is released, CI/CD is comprehensive (12 GitHub Actions workflows for testing, linting, release automation), and the codebase is well-maintained. However, it's a rewrite from the legacy Ruby t gem (in /legacy), so some API surface areas may still be stabilizing; recent commit activity and multi-platform binary releases (Linux, macOS, Windows ARM64/x86_64) indicate active development.
Low risk: Rust provides memory safety eliminating entire categories of bugs; single maintainer (sferik) is known in the Ruby community but dependency surface is small (clap, reqwest, serde for core functionality). Main risk: the codebase is a Rust rewrite of a mature Ruby tool, so edge cases in the legacy /legacy subdirectory may not be fully ported; OAuth token refresh logic is critical and any bugs there could break authentication workflows.
Active areas of work
Active v6.x development with recent bumps to Rust dependencies (clap 4.6.0, reqwest 0.13.3, serde_json 1.0.149), GitHub Actions workflows for Clippy linting, mutant testing, and cross-platform release automation. The transition from Ruby to Rust appears complete; focus is on stability and new X API v2 features (filtered streams, rules management).
🚀Get running
git clone https://github.com/sferik/x-cli.git
cd x-cli
cargo test
cargo run -- version
cargo run -- accounts --profile ~/.xrc
Daily commands:
Development: cargo run -- <command> [args] (e.g., cargo run -- accounts). Testing: cargo test. Production: cargo build --release outputs binary to target/release/x.
🗺️Map of the codebase
src/main.rs— Entry point for the CLI application; defines the main command structure and argument parsing using clapsrc/lib.rs— Library root exposing core CLI logic and public API; required for understanding module organizationCargo.toml— Defines all dependencies (reqwest, clap, serde, x-api) and project metadata; changes here affect the entire buildx-api— Local dependency providing HTTP client, authentication (OAuth 1.0a/2.0), and API retry logic; foundational to all API calls.github/workflows/test.yml— CI pipeline for testing; defines how the project is validated before release
🧩Components & responsibilities
- CLI Command Router (src/main.rs) (clap 4.6, Rust struct/enum enums) — Parses user arguments via clap, routes to appropriate command handler, formats and displays results
- Failure mode: Invalid arguments → clap prints usage/error; unhandled command panics
- Configuration Manager (src/lib.rs) — Loads, validates, and persists account profiles (~/.xrc,
🛠️How to make changes
Add a New CLI Command
- Define the command structure in src/main.rs using clap's command builder pattern or derive macros (
src/main.rs) - Implement the command handler in an appropriate module under src/, following the pattern of existing commands (
src/lib.rs) - Add authentication and API calls using the x-api crate's client primitives (OAuth setup, request/retry logic) (
x-api) - Write integration tests in tests/ directory to verify the command end-to-end (
Cargo.toml)
Add a New API Endpoint (x-api)
- Define the endpoint method in x-api module, exposing a typed client method (e.g., get_user_timeline) (
x-api) - Include OAuth 1.0a or 2.0 authentication wrapper based on API version (v1.1 or v2) (
x-api) - Leverage reqwest for HTTP and implement automatic retry with exponential backoff (
Cargo.toml) - Return structured serde-deserializable JSON responses to the calling CLI command (
src/lib.rs)
Add Profile/Account Configuration
- Extend the configuration file parser to read ~/.xrc (or ~/trc as fallback) in YAML format (
src/lib.rs) - Store account tokens, API keys, and active profile selection using serde_yaml_ng (
Cargo.toml) - Implement profile switching logic (set active, delete account) in the CLI command handlers (
src/main.rs)
Implement a Streaming Command
- Use reqwest's blocking HTTP client to open persistent connections to X's streaming endpoints (
x-api) - For filtered streams (search, list, timeline), manage rule lifecycle (add, delete, manage) before subscribing (
src/lib.rs) - Parse incoming events line-by-line (NDJSON) and deserialize with serde_json (
src/lib.rs) - Format and display events with column-aligned output; respect X_STREAM_MAX_EVENTS env var for test limits (
src/main.rs)
🔧Why these technologies
- Rust + clap 4.6 — Type-safe CLI framework with derive macro support; enables fast, safe compilation and minimal binary size
- reqwest 0.13 (blocking) — Battle-tested async HTTP client with built-in TLS, cookie handling, and redirect support; blocking variant keeps CLI code simple
- serde + serde_json + serde_yaml_ng — Zero-copy JSON/YAML deserialization; enables strong typing for API responses and config files
- OAuth 1.0a & 2.0 support — X API requires dual auth schemes; implemented in x-api to abstract complexity from CLI commands
- Local x-api crate dependency — Encapsulates HTTP primitives, auth, and retries; enables code reuse and testing without external binary dependencies
⚖️Trade-offs already made
-
Blocking HTTP (reqwest::blocking) instead of async
- Why: Simpler CLI code structure; avoids tokio/async overhead for request-response patterns
- Consequence: Cannot handle thousands of concurrent connections; suitable for CLI but not a web server
-
Local x-api crate instead of external crate registry
- Why: Tighter control over auth/retry logic; easier to evolve for X API changes without external dependency cycle
- Consequence: Developers must understand both repositories; increased maintenance burden
-
YAML config with ~/.xrc primary + ~/.trc fallback
- Why: Human-readable config; backward compatibility with legacy Ruby CLI
- Consequence: Two config formats must be maintained; migration logic required
-
Streaming via persistent HTTP (line-by-line NDJSON) not WebSocket
- Why: Simpler implementation; HTTP polling + connection reuse sufficient for CLI use cases
- Consequence: Higher latency for stream events compared to WebSocket; acceptable for CLI display
🚫Non-goals (don't propose these)
- Real-time collaborative editing (no WebSocket support planned)
- Web UI or browser-based interface
- Windows native shell integration (Fish/Zsh focus)
- Full X Spaces or paid API tiers support (v2 endpoints only)
- Backward compatibility with Ruby CLI binary name (Rust binary is 'x', not 't')
🪤Traps & gotchas
- OAuth setup required: X API credentials (API key, secret, access token) must be configured in ~/.xrc (YAML format) or ~/.trc (legacy INI format); missing credentials silently fail with unclear errors. 2. Streaming event limits: X_STREAM_MAX_EVENTS env var controls max events emitted (important for tests; no limit by default can hang CI). 3. Profile fallback chain: If ~/.xrc is missing, the tool reads ~/.trc (legacy Ruby format), then migrates on write—mixing formats can cause subtle inconsistencies. 4. Rust edition 2024: Cargo.toml specifies edition = "2024" which is cutting-edge; ensure your Rust toolchain is recent (run
rustup update). 5. x-api local dependency: Cargo won't fetch x-api from crates.io; it must be in the local x-api/ subdirectory or builds fail with path resolution errors.
🏗️Architecture
💡Concepts to learn
- OAuth 1.0a (3-legged flow) — x-cli uses OAuth 1.0a for V1.1 API endpoints; requires signing requests with consumer secrets and access tokens. Understanding HMAC-SHA1 signing and nonce generation is essential to debug auth failures.
- OAuth 2.0 Bearer Token & Refresh Token — x-cli supports OAuth 2.0 for V2 API endpoints; bearer tokens expire and must be refreshed. Examine x-api for refresh logic when tokens expire mid-request.
- Exponential backoff retry logic — x-cli automatically retries transient API errors (429 rate limits, 5xx) with exponential backoff. This is implemented in x-api; breaking it causes cascading failures.
- HTTP Streaming (chunked transfer encoding) — Stream commands (stream all, stream search) use persistent HTTP connections with Server-Sent Events. reqwest's blocking stream() API parses chunks as they arrive; closing the connection mid-stream is non-trivial.
- API versioning & fallback (V1.1 → V2) — x-cli supports both Twitter API V1.1 and V2; some endpoints exist in both versions with different schemas. The code must dispatch to the correct version and gracefully fall back if one is unavailable.
- Column-aligned output formatting — x-cli formats tabular API responses (tweets, lists, users) with aligned columns for readability. This is non-trivial when columns have variable width; examine src/lib.rs for the rendering logic.
- YAML configuration with fallback (profile migration) — x-cli reads ~/.xrc (YAML) but falls back to ~/.trc (legacy INI) if missing, then migrates on write. Understanding the deserialization chain prevents config loss.
🔗Related repos
sferik/t— The original Ruby implementation (now archived); x-cli is its Rust rewrite; understanding t's behavior is essential for backwards compatibilityegg-mode-rs/egg-mode— Pure Rust Twitter API client library; x-cli could potentially use this instead of the local x-api crate, or x-api could be extracted as a separate cratetwitter-rs/twitter-rs— Another Rust Twitter API wrapper (older, less maintained); x-cli's design avoids this because x-api is custom-built for CLI use casestwitterdev/twitter-api-go— Official Twitter API Go client; understanding official SDK patterns can inform x-api design decisions for missing endpoints or breaking API changesdavideasy/tweetable— Rust CLI for tweet composition; uses similar stack (clap, serde, reqwest); good reference for CLI UX patterns in Twitter tooling
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for OAuth 1.0a and OAuth 2.0 flows
The repo supports both OAuth 1.0a and OAuth 2.0 authentication (mentioned in README), but there are no visible integration tests for these critical auth flows. The x-api crate handles auth/HTTP primitives, but the actual CLI authentication workflows need test coverage to prevent regressions as the codebase evolves. This is high-value because auth is the foundation of the tool.
- [ ] Create tests/oauth_integration.rs for OAuth 1.0a flow testing against mock API endpoints
- [ ] Create tests/oauth2_integration.rs for OAuth 2.0 flow testing
- [ ] Add test fixtures in tests/fixtures/ for mock credentials and API responses
- [ ] Update .github/workflows/test.yml to ensure these integration tests run in CI
- [ ] Document test execution steps in CONTRIBUTING.md (if it exists) or README.md
Add completion script generation for zsh and bash (like the legacy Ruby version)
The repo has etc/t-completion.fish for Fish shell, but the legacy version had etc/t-completion.sh and etc/t-completion.zsh. New contributors would benefit from Rust-based shell completion generation. The dev-dependency clap_mangen is already present but unused—it can generate man pages and shell completions. This would provide parity with the legacy tool.
- [ ] Create src/bin/completions.rs to generate shell completions using clap_mangen
- [ ] Add build step in build.rs to auto-generate bash, zsh, and fish completions
- [ ] Create etc/x-completion.bash and etc/x-completion.zsh
- [ ] Update README.md with installation instructions for each shell
- [ ] Add GitHub Action workflow .github/workflows/completion-gen.yml to verify completions generate without error
Add unit tests for stream command implementations (stream/all, stream/search, etc.)
The README indicates stream commands use persistent HTTP streaming with different endpoints (OAuth2 sample vs v2 filtered stream rules). However, there are no visible test files for the stream command logic. The streaming logic is complex (handling X_STREAM_MAX_EVENTS env var, different auth methods per stream type, event parsing) and deserves focused test coverage to catch parsing or event-handling regressions.
- [ ] Create tests/stream_all_test.rs for OAuth2 sample stream behavior
- [ ] Create tests/stream_search_test.rs for v2 filtered stream rules + streaming
- [ ] Create tests/stream_max_events_test.rs to verify X_STREAM_MAX_EVENTS env var limits output
- [ ] Add mock HTTP streaming responses in tests/fixtures/stream_responses/
- [ ] Verify tests run in .github/workflows/test.yml and pass with mocked reqwest responses
🌿Good first issues
- Add integration tests for OAuth 2.0 refresh token flow in tests/; currently only OAuth 1.0a is exercised. Examine src/lib.rs for auth dispatch logic and write tests that mock X API token endpoint responses.
- Write user-facing docs for the ~/.xrc YAML config schema (field names, required vs optional, example profiles for different OAuth flows). Currently only implied in src/lib.rs config parsing; see /legacy/README.md for hints.
- Add a
x config validatesubcommand that checks ~/.xrc syntax, verifies API credentials without making API calls, and suggests fixes. Requires new clap subcommand in src/main.rs + validation logic in src/lib.rs.
📝Recent commits
Click to expand
Recent commits
8a6bbd3— deps: bump rustls-webpki from 0.103.12 to 0.103.13 (dependabot[bot])17141cf— deps: bump reqwest from 0.13.2 to 0.13.3 (dependabot[bot])2cd3ed9— cargo update (sferik)4389149— Fix toolchain freshness check parsing wrong version (sferik)1356bd9— deps: bump rand from 0.9.2 to 0.9.4 (dependabot[bot])19450dd— ci: bump softprops/action-gh-release from 2 to 3 (dependabot[bot])968c5f4— Fix broken pipe in toolchain freshness CI check (sferik)b541b2e— Add Windows ARM64 (aarch64-pc-windows-msvc) release binary (sferik)0ec0cb1— Define command descriptions in Rust instead of deriving from legacy Ruby files (sferik)02f05ea— Add man page (sferik)
🔒Security observations
The x-cli project shows reasonable security practices with modern Rust tooling, dependency management via Cargo, and CI/CD workflows. However, there are concerns around YAML deserialization, configuration file handling, and test credentials in version control. The invalid Rust edition in Cargo.toml is a configuration error that suggests insufficient validation in the build process. No SQL injection, XSS, or Docker-specific vulnerabilities were identified. The main risks relate to unsafe deserialization of user configuration and credential management.
- Medium · Deprecated Rust Edition —
Cargo.toml. Cargo.toml specifies edition = '2024', which does not exist. Valid Rust editions are 2015, 2018, and 2021. This may cause compilation failures and is likely a typo. This could indicate insufficient testing or CI validation. Fix: Update to a valid Rust edition: 2021 is recommended for modern projects. Change 'edition = "2024"' to 'edition = "2021"'. - Medium · Unvalidated Local File Access —
Cargo.toml (dirs dependency), configuration file handling. The codebase uses 'dirs' crate to access user home directories (~/.xrc, ~/.xrc_multi, etc.) for configuration. Without proper input validation and path traversal prevention, malicious YAML configuration files could potentially execute unintended operations or expose sensitive data. Fix: Implement strict validation of configuration file paths, use canonicalize() to prevent path traversal attacks, and validate all configuration file contents before processing, especially YAML deserialization which can be vulnerable. - Medium · Insecure Deserialization (YAML) —
Cargo.toml (serde_yaml_ng dependency), configuration handling. The project uses 'serde_yaml_ng' for YAML configuration parsing. YAML deserialization can be exploited for arbitrary code execution if untrusted YAML files are processed. The configuration files stored in user home directory (~/.xrc) could be modified by other processes or compromised accounts. Fix: Validate and sanitize YAML configuration before deserialization, consider using a safer configuration format like TOML, restrict file permissions on configuration files (chmod 600), and implement strict schema validation. - Low · Hardcoded Test Credentials in Version Control —
legacy/test/fixtures/. Test fixtures in legacy/test/fixtures/ directory contain configuration files (.trc, .trc_multi, .trc_set) and access tokens (access_token, bearer_token.json) that appear to be stored in version control. Even if these are test credentials, they may serve as examples of real credential patterns. Fix: Remove all actual credentials from version control, use environment variables or secrets management for tests, add .trc files and token files to .gitignore, consider using tools like git-secrets or TruffleHog to scan history. - Low · Missing HTTPS Enforcement —
Cargo.toml (reqwest dependency), x-api library usage. The project uses 'reqwest' for HTTP requests and 'serde_urlencoded' for URL encoding. While reqwest defaults to HTTPS, there's no explicit enforcement or verification that API calls to X (Twitter) API always use HTTPS. Fix: Ensure all API endpoints are accessed via HTTPS, implement certificate pinning for critical API endpoints, disable support for insecure protocols, and document security requirements in the x-api crate. - Low · Insufficient Dependency Version Pinning —
Cargo.toml. Dependencies use minor version constraints (e.g., '4.6.0', '0.4.44') rather than patch-level constraints. This allows automatic updates to new minor versions which could introduce breaking changes or security issues without explicit review. Fix: Review and consider more conservative versioning strategy: use '=X.Y.Z' for critical dependencies, regularly audit Cargo.lock file for updates, and enable dependabot security alerts in GitHub settings (already partially configured).
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.