vaikhari वैखरी
posting

vaikhari

वैखरी · “speech spoken aloud”

Write once, post to X and LinkedIn from the terminal: a live preview of each, drafts, a schedule, and an agent that drafts for you. Free.

v0.2.0 latest3 releases4 decisions recordedMIT license

#vaikhari

Write once, post to X and LinkedIn from the terminal. One editor with a live preview of how each network will show your post, drafts, a schedule, and an AI agent that can draft posts for you (you approve every one). Free: LinkedIn through its free API, X through its own share page with one click.

vaikhari's Compose tab: the post on the left, live previews of how X and LinkedIn will show it on the right, with the character counts

The Queue: an agent's post waiting for your approval, with its reason, and a post scheduled for tomorrow

Screenshots use vaikhari demo: a made-up account.

Vaikhari (वैखरी) is Sanskrit for "spreading out, broadcast".

#What it does

#Install

With kosha:

kosha install vaikhari

Or with the installer, which downloads the release binary into ~/.local/share/vaikhari (or builds it with Go) and links ~/.local/bin/vaikhari:

curl -fsSL https://raw.githubusercontent.com/Theyashsawarkar/vaikhari/main/install.sh | bash

From source: make && make install (Go 1.26+).

As a tmux plugin (TPM), prefix B opens it in a popup:

set -g @plugin 'Theyashsawarkar/vaikhari'

The plugin runs the vaikhari on your PATH, or builds its own copy with Go the first time.

Optional: wl-clipboard (Wayland) or xclip (X11) for images on X and copying links, notify-send (libnotify) for notifications, secret-tool (libsecret) to keep the LinkedIn sign-in in your keyring.

Try it first without any account: vaikhari demo runs it with a fake LinkedIn in a throwaway folder (nothing is posted, X isn't opened).

#Set up LinkedIn

vaikhari posts to LinkedIn through an app of your own: free, self-serve, about two minutes, once (ADR 4).

vaikhari auth linkedin --setup

It walks you through it:

  1. At linkedin.com/developers/apps/new create an app: any name, a LinkedIn page as its company (LinkedIn needs one; one you create works) and a logo.
  2. On the Products tab add Share on LinkedIn and Sign In with LinkedIn using OpenID Connect (both approved at once).
  3. On the Auth tab add http://localhost:8723/callback to "Authorized redirect URLs", and copy the Client ID and Primary Client Secret into vaikhari.

Then your browser opens on LinkedIn's sign-in. LinkedIn's sign-in lasts 60 days and can't be renewed silently: the screen's top right shows the days left, turning yellow in the last week, and L (or vaikhari auth linkedin) signs in again.

vaikhari posts to your personal profile. Company pages need LinkedIn's review and aren't supported.

#Posting to X: one click

X has no free API any more, so vaikhari never logs in to X. When a post goes out:

  1. X's share page opens in your browser with the post filled in (the first image, if any, is on your clipboard: Ctrl+V pastes it).
  2. You press Post there.
  3. Back in vaikhari, press Enter. Paste the post's link first if you want it kept; for a thread, the link makes the next part a reply to it.

Esc leaves it for later (it waits in Queue, o opens it again); Ctrl+K skips X for that post. A scheduled X post can't go out without you: at its time a notification offers to open it.

#The screen

vaikhari (or prefix B in tmux) opens on Compose, in the editor:

The top right says who LinkedIn posts as (and days left) and that X is one click. Tabs count what's in Queue and Drafts.

#Keys

? in the screen shows them all.

While writing

KeyAction
Ctrl+SPost it now (LinkedIn goes out, X opens in your browser; asks first)
Ctrl+TSchedule it (tomorrow 9am, fri 6pm, in 2h...)
Ctrl+DSave it as a draft
Ctrl+XToggle posting to X
Ctrl+LToggle posting to LinkedIn
Ctrl+OAdd an image (a path; Ctrl+O on an empty line removes them)
Ctrl+NStart a new post (the current one stays as it was saved)
EscLeave the editor (the other keys work then)

Compose, outside the editor

KeyAction
i / EnterEdit the post

Everywhere (outside the editor)

KeyAction
Tab / Shift+TabSwitch to the next / previous tab
1 – 4Switch to Compose, Queue, Drafts or Posted
LOpen LinkedIn's sign-in again (it lasts 60 days)
?Show every key
qQuit

Queue, Drafts and Posted

KeyAction
j / kMove to the next / previous post
Enter / eEdit the post in Compose
pPost it now (asks first)
tSchedule it, or move it to another time
oOpen it on X's page to press Post (and confirm it after)
aApprove an agent's post
nDecline an agent's post
rSend a post LinkedIn refused again
cCancel a scheduled or waiting post
dDelete the draft (asks first)
yCopy the post's link
vOpen the post's link in the browser

While X waits for your click

KeyAction
EnterConfirm it's posted on X (paste its link first for a thread's next part)
Ctrl+OOpen X's page again
Ctrl+KCancel X for this post (LinkedIn is unaffected)
EscLeave it for later (it waits in Queue)

#From the shell

vaikhari post "Shipped it #golang"                 # both networks (or config's `networks`)
vaikhari post --linkedin "Only on LinkedIn"
vaikhari post --x --image shot.png "With a picture"
vaikhari post --at "fri 6pm" "Later"               # scheduled
vaikhari post --draft "Not yet"
echo "From a file" | vaikhari post -               # text on stdin
vaikhari check "Will this fit?"                    # X count and thread, LinkedIn count and fold
vaikhari list [draft|scheduled|approval|ready|posted|failed]
vaikhari approve [ID]                              # an agent's post (the oldest waiting)
CommandWhat it does
vaikhariThe screen (--popup KEY: in a tmux popup, KEY closes it)
vaikhari post TEXTPost (--x, --linkedin, --at WHEN, --image PATH repeated, --draft); for X it opens the page and asks you to confirm
vaikhari check TEXTHow the text fits each network
vaikhari list [STATUS]Your posts, one per line
vaikhari approve [ID]Answer an agent's post
vaikhari auth linkedin [--setup]Sign in to LinkedIn (--setup: enter the app's id and secret)
vaikhari auth status / logoutWhat's signed in / sign out of LinkedIn
vaikhari service install|remove|statusThe background service that publishes scheduled posts
vaikhari daemonThe same, in the foreground
vaikhari mcp [--requester NAME]The MCP server for agents (stdio)
vaikhari doctorCheck the setup: LinkedIn, clipboard, notifications, the service
vaikhari demoTry it with a fake LinkedIn
vaikhari keybindingsThe keys, for Vayu's keybinding search
vaikhari --versionThe version

Times (--at, Ctrl+T, t) read the way people write them: in 2h, tomorrow 9am, fri 6pm, next tue 10:00, tonight, 3 nov 10:30, 2026-11-03 18:00. A day without a time means 9:00.

#The schedule

Scheduled posts go out while vaikhari runs (the screen, vaikhari mcp), or at any time with the background service:

vaikhari service install      # ~/.config/systemd/user/vaikhari.service, started now and at login
journalctl --user -u vaikhari -f

At a post's time, LinkedIn goes out by itself and you get a notification; X waits for you (the notification's "Open in browser" opens it). A post is claimed before it's sent, so the screen, the MCP server and the service never publish one twice.

#For AI agents (MCP)

claude mcp add --scope user vaikhari -- vaikhari mcp --requester "Claude Code"
ToolWhat it does
post_checkLengths, X's thread split, LinkedIn's fold: to fit a post before proposing it
post_draftSave a draft you finish in vaikhari
post_publishPropose a post, now or send_at later: you approve it first (a tmux popup on the agent's pane, and a notification), and may edit or decline it; the call waits up to 10 minutes and reports what happened
post_listYour posts and their links
post_accountsLinkedIn's sign-in and days left; X's one click

Agents never post on their own, and are told X needs your click. What they did is logged in ~/.local/state/vaikhari/agent.log.

#Configuration

~/.config/vaikhari/config.toml (written by vaikhari auth linkedin --setup, private to you):

networks = ["x", "linkedin"]   # ticked in a new post

[linkedin]
client_id = "..."
client_secret = "..."
port = 8723                    # the redirect: http://localhost:<port>/callback
version = "202607"             # LinkedIn API version (YYYYMM); LinkedIn retires each after about a year

[x]
name = "Your Name"             # shown in the X preview (default: your LinkedIn name)
handle = "you"                 # shown as @you in the X preview
EnvironmentWhat it does
VAIKHARI_HOMEKeep config, posts and state under one folder
VAIKHARI_OPENERThe program that opens links (default xdg-open)
VAIKHARI_NO_KEYRINGKeep the LinkedIn sign-in in a file, not the keyring
VAIKHARI_LINKEDIN_URLTalk to another LinkedIn (the tests' fake)
NO_NERD_FONTSquare key caps instead of Nerd Font round ones

tmux plugin options (set before TPM's run line):

OptionDefaultWhat it does
@vaikhari-keyBKey after the prefix (none: no key)
@vaikhari-width / @vaikhari-height90% / 88%Popup size
@vaikhari-title vaikhari Popup title
@vaikhari-launcherYour own popup command instead of display-popup

#Files

PathWhat
~/.config/vaikhari/config.tomlSettings and your LinkedIn app (0600)
~/.local/share/vaikhari/vaikhari.dbYour posts (SQLite, 0600)
keyring, or ~/.local/share/vaikhari/linkedin.tokenThe LinkedIn sign-in
~/.local/state/vaikhari/agent.logWhat agents asked for, and your answers
~/.config/systemd/user/vaikhari.serviceThe background service (service install)

#How it works

One Go binary: the screen (Bubble Tea), the CLI, the MCP server and the service share internal/app, which keeps posts in SQLite and moves them between draft, approval, scheduled, ready (X waits for your click), posted, failed, cancelled and declined only through checked transitions. internal/post knows each network's rules (X's weighted count and thread splitting, LinkedIn's "little text" escaping and hashtags), internal/linkedin the API, and internal/fake a fake LinkedIn for the tests and vaikhari demo. Decisions are recorded in docs/adr.

#Troubleshooting

#License

MIT