prasar प्रसार
posting

prasar

प्रसार · “broadcast”

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.1.1 latest2 releases4 decisions recordedMIT license

#prasar

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.

Prasar (प्रसार) is Sanskrit for "spreading out, broadcast".

#What it does

#Install

With kosha:

kosha install prasar

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

curl -fsSL https://raw.githubusercontent.com/Theyashsawarkar/prasar/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/prasar'

The plugin runs the prasar 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: prasar demo runs it with a fake LinkedIn in a throwaway folder (nothing is posted, X isn't opened).

#Set up LinkedIn

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

prasar 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 prasar.

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 prasar auth linkedin) signs in again.

prasar 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 prasar 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 prasar, 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

prasar (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

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

prasar service install      # ~/.config/systemd/user/prasar.service, started now and at login
journalctl --user -u prasar -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 prasar -- prasar 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 prasar
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/prasar/agent.log.

#Configuration

~/.config/prasar/config.toml (written by prasar 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
PRASAR_HOMEKeep config, posts and state under one folder
PRASAR_OPENERThe program that opens links (default xdg-open)
PRASAR_NO_KEYRINGKeep the LinkedIn sign-in in a file, not the keyring
PRASAR_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
@prasar-keyBKey after the prefix (none: no key)
@prasar-width / @prasar-height90% / 88%Popup size
@prasar-title prasar Popup title
@prasar-launcherYour own popup command instead of display-popup

#Files

PathWhat
~/.config/prasar/config.tomlSettings and your LinkedIn app (0600)
~/.local/share/prasar/prasar.dbYour posts (SQLite, 0600)
keyring, or ~/.local/share/prasar/linkedin.tokenThe LinkedIn sign-in
~/.local/state/prasar/agent.logWhat agents asked for, and your answers
~/.config/systemd/user/prasar.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 prasar demo. Decisions are recorded in docs/adr.

#Troubleshooting

#License

MIT