Loading repository data…
Loading repository data…
tarkaai / repository
Local-first, AI-native LinkedIn messenger CLI. Sync your inbox to plain JSONL files in a git repo, pipe to agents, send from the command line.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
LinkedIn messenger from the CLI. File-backed, git-versioned, designed for AI agents and humans.
Messages are stored locally as JSONL files in a git repository. All reads come from disk. Network calls go to LinkedIn's private APIs using cookies captured via a headed Playwright browser session.
Want a terminal inbox UI on top of this? See the companion repo,
tarkaai/allman-tui. It bundles theallmanbinary, so installing the TUI gets you both.
curl -fsSL https://raw.githubusercontent.com/tarkaai/allman-cli/main/install.sh | bash
Pin a version or change the install prefix:
curl -fsSL .../install.sh | VERSION=2026-04-20.1-alpha bash
curl -fsSL .../install.sh | PREFIX=$HOME/.local bash
Or grab a binary directly from the releases page.
Requires Bun (latest, via asdf or direct install).
git clone git@github.com:tarkaai/allman-cli.git
cd allman-cli
bun install
bun run build # produces dist/allman
Add dist/allman to your $PATH, or run directly with:
bun run dev -- <command> [options]
Playwright's Chromium browser is required for login only:
allman install-browsers
# or: bunx playwright install chromium
allman login # opens browser, saves session
allman sync # pull conversation history (default: since last sync)
allman conversations # list conversations
allman messages sarah-chen # show messages with sarah-chen
allman send sarah-chen "Hey!" # send a message
allman listen # stream real-time events to stdout as NDJSON
All commands accept these flags:
| Flag | Description |
|---|---|
-a, --account <slug> | Account to use (default: $ALLMAN_ACCOUNT, or the only account if there is one) |
-s, --store <path> | Store directory (default: $ALLMAN_STORE, or ./.allman) |
--json | Output as machine-readable JSON (stdout) |
--debug | Verbose debug output to stderr |
allman loginAuthenticate with LinkedIn. Opens a headed Chromium browser. Complete the login in the browser window; cookies are captured automatically.
allman login
allman login --account your-account
allman login --proxy host:port
allman login --proxy host:port:username:password
On success, writes AUTH.json and COOKIES.json to the store and creates a slug symlink for the account.
Options:
| Flag | Description |
|---|---|
--account <slug> | Account name to create or re-authenticate |
--proxy <host:port[:user:pass]> | HTTP proxy for this account (saved to config.json) |
allman logoutClear session cookies for an account. Does not delete message history.
allman logout
allman logout --account your-account
allman statusShow authentication status for one or all accounts.
allman status
allman status --account your-account
allman status --json
Output includes: profile slug, name, auth status, cookie validity, last sync time, proxy, and store path.
allman startVerify auth (login if needed), sync from the last sync date, then run listen indefinitely. Designed as a single entrypoint for daemon use.
allman start
allman start --account your-account
allman syncPull conversation history from LinkedIn into the local store.
allman sync # sync all conversations since last sync
allman sync --since 3mo # sync all conversations from 3 months ago
allman sync --since 2025-01-01 # sync from a specific date
allman sync sarah-chen # sync only this conversation
Behavior:
lastSyncAt in AUTH.json. Falls back to 90 days if no prior sync.publicIdentifier (slug) via the profile API, with exponential backoff on rate limits.RECORD.json and messages/YYYY-MM.jsonl files for each conversation.Options:
| Flag | Description |
|---|---|
[conversation] | Sync a single conversation (slug, profileId, or convId) |
--since <duration|date> | Duration (1h, 3d, 1w, 3mo, 1y) or ISO date (YYYY-MM-DD) |
allman listenStream real-time LinkedIn events to stdout as NDJSON. All logs go to stderr.
allman listen
allman listen --account your-account
# Pipe to a handler
allman listen | while read -r event; do
echo "Event: $event"
done
Event types emitted:
| Event | Description |
|---|---|
connected | SSE connection established |
heartbeat | Keep-alive (every 60s) |
message.received | Inbound message from a contact |
message.sent | Outbound message echo (confirming delivery) |
typing | Contact is typing |
read_receipt | Contact read a message |
reaction | Reaction added or removed |
Each event is a JSON object on a single line. Example:
{"event":"message.received","account":"ACoAA...","timestamp":1704067200000,"conversation":{"urn":"urn:li:messagingThread:...","convId":"2-abc123","name":"Sarah Chen","slug":"sarah-chen"},"from":{"urn":"urn:li:fsd_profile:...","name":"Sarah Chen"},"message":{"urn":"urn:li:messagingMessage:...","body":"Hey, got a minute?","isFromMe":false}}
Behavior:
INBOX.jsonl (gitignored) for hook-based integrations.allman conversations (alias: convs)List conversations from the local store, sorted by most recent activity.
allman conversations
allman conversations --limit 20
allman conversations --json
Options:
| Flag | Default | Description |
|---|---|---|
-n, --limit <n> | 50 | Max conversations to show |
allman messages <conversation> (alias: msgs)Show messages for a conversation. Auto-syncs if the conversation is not found locally or if the last sync was more than 1 minute ago.
allman messages sarah-chen
allman messages sarah-chen --limit 100
allman messages sarah-chen --since 2025-01-01
allman messages "https://www.linkedin.com/in/sarah-chen"
allman messages "urn:li:messagingThread:2-abc123"
allman messages sarah-chen --no-sync # skip auto-sync
The <conversation> argument accepts:
sarah-chen)https://www.linkedin.com/in/sarah-chen)urn:li:messagingThread:2-abc123)Options:
| Flag | Default | Description |
|---|---|---|
-n, --limit <n> | 50 | Max messages to show |
--since <date> | — | ISO date filter |
--no-sync | — | Skip auto-sync |
allman send <to> <text>Send a message to a LinkedIn contact.
allman send sarah-chen "Hey, how are you?"
allman send "https://www.linkedin.com/in/sarah-chen" "Hello!"
allman send "urn:li:messagingThread:2-abc123" "Following up"
The <to> argument accepts a slug, profile URL, or conversation URN.
Behavior:
rate-state.json across process restarts.allman search <query>Search contacts and conversations by name, slug, or profile ID. Fuzzy matching with confidence scores.
allman search "sarah"
allman search "ali smi" # matches "Alice Smith"
allman search sarah-chen
allman search --limit 5 "sarah"
allman search --json "sarah"
Confidence scoring:
| Score | Match type |
|---|---|
| 100 | Exact slug or profile ID |
| 95 | Exact name (case-insensitive) |
| 80 | Name starts with query |
| 70 | Every query word matches a word start in name ("ali smi" → "Alice Smith") |
| 60 | Name or slug contains query as substring |
| 40 | Any query word found in name or slug |
Options:
| Flag | Default | Description |
|---|---|---|
-n, --limit <n> | 10 | Max results |
allman inboxShow new messages since the last time inbox was run (watermark-based). Syncs first, then scans all conversations for inbound messages newer than the watermark.
allman inbox
allman inbox --since 1h # override watermark
allman inbox --no-mark # don't advance the watermark
allman inbox --limit 10
allman inbox --json
The watermark is stored in inbox-state.json (gitignored). On first run, defaults to 24 hours ago.
Per-conversation read tracking: if you sent a message to a conversation, that conversation is considered read up to the time of your send, so it won't appear in inbox unless a reply arrives after that.
Options:
| Flag | Description |
|---|---|
--since <duration|date> | Override watermark (1h, 3d, 1w, or ISO date) |
--no-mark | Don't advance the watermark after viewing |
-n, --limit <n> | Max conversations to show |
allman grep <query>Full-text search across all locally stored message bodies. Scans JSONL files directly.
allman grep "project proposal"
allman grep "contract" --since 3mo
allman grep "meeting" --limit 100
allman grep "invoice" --json
Results are sorted newest-first.
Options:
| Flag | Default | Description |
|---|---|---|
--since <duration|date> | — | Only search messages after this date |
-n, --limit <n> | 50 | Max results |
allman connectionsExport your 1st-degree LinkedIn connections into the store as per-connection records with slug symlinks. Uses the flagship relationships/dash/connections API (IDs + slug only — no profile-page scraping). Random 2–8s delay between pages.
allman connections # store all connections (per-file + symlinks), git-committed
allman connections --limit 500 # cap the number fetched
allman connections --csv ./conns.csv # also export a CSV
allman connections --no-save --csv x.csv # CSV only, don't write the store
allman connections --json # stream NDJSON to stdout (ephemeral; no store write)
allman connections --include-headline # also store the headline
Writes {myProfileId}/connections/{flagshipId}.json per connection plus a {slug} -> {flagshipId}.json symlink. Re-running is idempotent: firstSeenAt is preserved and lastSeenAt is refreshed.
Options:
| Flag | Default | Description |
|---|---|---|
-n, --limit <n> | all | Max connections to fetch |
--page-size <n> | 100 | Results per request (max 500) |
--csv <path> | — | Also export a CSV to this path |
--no-save | — | Don't write to the store (use with --csv) |
--include-headline | — | Include the LinkedIn headline |
--json | — | Stream NDJSON to stdout ( |