For AI assistants: Read
AGENTS.mdfirst (or run.\telelink.ps1 agent context). Do not explore the codebase before running CLI commands. Setup from scratch:.skills/telelink/SETUP.md. Full skill:.cursor/skills/telelink/SKILL.md.
Your iPhone on Windows β for you and your AI agents.
TeleLink is a local Windows CLI that bridges Phone Link (iPhone paired to your PC) with your terminal, scripts, and coding assistants like Cursor and Claude Code. It watches incoming messages, sends SMS on your behalf, schedules messages for later, moves files between phone and PC, and keeps structured data on disk so agents can read notifications and act without a cloud service.
No Mac. No paid APIs. Everything stays on your machine.
| Capability | What it means |
|---|---|
| Watch & capture | Live notification watcher β structured log (caught.jsonl), attachments, contact tags |
| Send SMS | send opens Phone Link compose; optional UI automation taps Send for agents |
| Intake | Phone β PC over WiβFi: web form, API, or iOS Shortcut pushes text, links, and images |
| Outtake | PC β phone: upload files to GitHub and show a QR code or SMS link |
| Contacts | Match senders to people; route, filter, and resolve names for send |
| Calendar | Schedule SMS for later; optional Google Calendar two-way sync |
| Call log | Phone Link call toasts β calls.jsonl; calls / missed commands and reminders |
| Agent-ready | Short-lived vision snapshots (.analyze/), owner tone, @ shortcuts in messages |
βββββββββββββββββββββββββββββββββββββββββββ
iPhone βββββββββββΊβ Phone Link (Windows) β
β toasts Β· SMS compose Β· photo folders β
ββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββ
βΌ βΌ βΌ
watcher send / sync harvest
(notifications) (SMS via Phone Link) (DB + folder images)
β β β
ββββββββββββββ¬βββββββββββββ΄ββββββββββββββββββββββββββ
βΌ
data/caught.jsonl Β· contacts.jsonl Β· calls.jsonl Β· attachments/
β
ββββΊ data/.analyze/ (AI snapshot β Cursor / Claude)
iPhone ββ WiβFi βββΊ intake :8787 βββΊ same caught + attachments pipeline
PC files ββββββββββΊ outtake (GitHub QR/SMS)
calendar ββββββββββΊ scheduled SMS (SQLite) βββΊ fires via send / Phone Link
βββ optional β Google Calendar (two-way sync)
Phone Link calls ββΊ watcher βββΊ data/calls.jsonl (missed / incoming / outgoing)
TeleLink is designed so an agent on your PC can see what your phone received and respond without copy-pasting from Phone Link.
Read what arrived
caughtβ browse notifications as a table, JSON, or filtered viewsdata/.analyze/latest.json+latest.pngβ small centered preview of the last attachment (vision-friendly; auto-expires)data/caught.jsonlβ full structured history
Send on your behalf
send "Client Name" Running about 10 minutes late, thanks for waiting
send +15551234567 On my way
send in%2hour% girlfriend Hey bro this is an example message
send in%<when>% schedules via the calendar (does not send immediately). send <who> <msg> still sends now. Case-insensitive (IN%2HOUR%). Optional % around the contact: send in%30m% %girlfriend% On my way.
- Resolves names from
data/contacts.jsonl messaging.method: autouses UI automation to click Send in Phone Link (best-effort; see limits below)- Set
owner.talking_tonein config so agents draft SMS in your voice
Schedule for later
calendar add love-note girlfriend "2pm today" "I love you! See you tonight."
calendar add follow-up "Client" "in 3h" "Just sent the files β let me know."
- Events live in SQLite and fire automatically while TeleLink is running (shell or watcher)
- Optional Google Calendar two-way sync β edit in either place, both stay aligned
Share files both ways
- Intake β iPhone uploads to your PC (Shortcut, browser, or
curl) - Outtake β PC uploads to GitHub; phone scans QR or opens SMS link
Install the project skill for agents: .cursor/skills/telelink/ (copy to ~/.cursor/skills/telelink/ for all projects). Run how or help <topic> inside TeleLink for the full in-terminal guide.
| Command | Description |
|---|---|
watcher |
Start notification + folder watchers (new console window) |
watcher stop |
Force stop the watcher window |
listen / listen stop |
Aliases for watcher / watcher stop |
caught |
Notifications viewer (table / json / gron) |
caught --json / --raw / --gron |
JSON table, raw lines, gron format |
caught --contacts-only |
Only matched-contact notifications |
caught --group channel |
Group by platform (iMessage, Instagram, β¦) |
caught last 5 |
Recent rows (shell syntax) |
contacts |
List contacts and match rules |
contacts add Name +1β¦ |
Add or update a contact |
contacts --sync-caught |
Import senders from caught log |
send <who> <msg> |
SMS via Phone Link (quote names with spaces) |
calendar |
Table of pending scheduled SMS events |
calendar add <name> <who> <when> <msg> |
Schedule a message (2pm today, in 3h, tomorrow 9am, β¦) |
calendar delay <name> <when> |
Postpone a pending event |
calendar delete <name> |
Remove a scheduled event |
calendar cancel <name> |
Cancel (keeps row, marks cancelled) |
calendar <name> status |
One event β fires at, countdown, errors |
calendar status |
Calendar health + Google sync state |
calendar connect google |
OAuth to Google Calendar (optional) |
calendar sync |
Force two-way Google Calendar sync |
calls |
Recent Phone Link calls (default 1d retention) |
calls --limit 20 / calls 20 |
Limit rows (shell or CLI) |
missed |
Missed calls only β who called and when |
missed --limit 10 / missed 10 |
Limit missed rows |
sync |
Re-sync config + jsonl + sqlite + Phone Link DB |
sync --caught |
Also import caught senders into contacts |
config |
Status dashboard (paths, intake URL, harvest) |
config edit <key> <value> |
Set config.yaml (auto-save) |
config get <key> / config list |
Read settings |
doctor |
Alias for config |
intake |
LAN upload server β menu: QR or SMS link |
intake --qr / --sms / --wait |
Skip menu; --wait until Ctrl+C |
intake global |
Public URL via cloudflared/ngrok |
intake stop |
Stop server + tunnel |
outtake <url|fileβ¦> |
QR/SMS share; multiple files β zip β GitHub |
how |
Full terminal how-to guide |
help <topic> |
e.g. help intake, help calendar, help calls, help analyze |
wipe calls |
Clear data/calls.jsonl |
status |
Quick caught summary |
Shell keys: ctrl+h help Β· ctrl+b back Β· ctrl+c cancel Β· clear / cc clears screen.
The watcher is a separate console window that must stay open while you want live capture.
- Toasts β WinRT notification listener (PowerShell fallback); call toasts (missed, incoming, outgoing) go to
data/calls.jsonl, notcaught - Images β watches Phone Link photo folders; optional harvest polls Phone Link SQLite for blobs Phone Link never synced
- Contacts β tags each row when
matchrules hit indata/contacts.jsonl - Preview β inline image preview in the watcher window (configurable)
- Analyze β writes
data/.analyze/latest.png+latest.jsonfor each new attachment (short TTL)
python -m telelink watcher
python -m telelink watcher --foreground # same terminal, no new window
python -m telelink caught
python -m telelink caught contacts
python -m telelink caught --jsonrouting.only_contacts: true skips notifications that do not match any managed contact.
send "Shawk" Running 10 min late
send +15551234567 Hello from TeleLink
How it works
- Resolves
<who>againstdata/contacts.jsonl(name or phone) - Expands @ shortcuts in the message body from
ownerin config (see below) - Opens Phone Link SMS compose with the number and text filled
- With
messaging.method: auto, UI automation (pywinauto) clicks Send β best effort; verify on the phone
Limits (iPhone + Phone Link)
Microsoft does not expose a silent send API. Apple may still require confirmation on the iPhone. TeleLink automates what Phone Link allows; it cannot bypass carrier or Apple restrictions. See assets/messaging-help.txt.
messaging:
method: auto # manual | auto | adb (Android USB only)
auto_timeout_seconds: 12Use these in the message body; TeleLink expands them before compose opens:
| Tag | Expands to |
|---|---|
@nameshort |
owner.personal_name_short |
@namefull |
owner.personal_name_full |
@business |
owner.business_name |
@email |
owner.work_email |
@timezone |
owner.timezone |
@tone |
owner.talking_tone (full string β usually for agents, not literal SMS) |
Also: /n = newline in the message body.
For AI agents: read owner.talking_tone and draft the SMS in that voice. Use @nameshort / @business in the body; do not paste @tone unless you want the tone text inside the message.
config edit tone Professional, direct, human tone. No em dashes.
send "Client" Hi @nameshort here β running about 10 minutes late
Schedule text messages to send later. TeleLink stores events in SQLite, fires them through the same send / Phone Link path when due, and can optionally keep everything in sync with Google Calendar.
- You (or an agent) run
calendar addwith a short name, who (contact or+number), when, and message - The event is saved as
pendingindata/telelink.db(calendar_eventstable) - While TeleLink is running, a background scheduler checks every 30 seconds (configurable)
- When
fire_atis reached, TeleLink callssendβ same compose / auto-send behavior as immediate SMS - Status becomes
completedorfailed(never returned topending; stuck in-flight rows are marked failed without retry)
Important: the interactive shell or watcher session must stay open for events to fire. A one-shot python -m telelink calendar add β¦ without a running shell will not send at the scheduled time.
Every 15 minutes, if any events are still pending, the shell prints a reminder (same style as the intake server uptime label):
CALENDAR 2 scheduled events waiting to fire
View: calendar Β· cancel: calendar delete <name>
calendar # table of pending events
calendar add love-note girlfriend "in 3h" "I love you!"
calendar add dinner "Mom" "2pm today" "Running 10 min late"
calendar delay love-note 1h # postpone
calendar delete love-note # remove
calendar love-note status # one event detail
calendar status # scheduler + Google health
Google sync is off by default. When enabled, TeleLink and Google Calendar stay in two-way sync:
- TeleLink β Google β pending events appear as calendar events with
[telelink]metadata - Google β TeleLink β time, title, and description edits update the local event; cancellations sync back
- Delete β
calendar deleteremoves the linked Google event - Only TeleLink-managed events are imported (not your entire Google calendar)
calendar:
enabled: true
poll_seconds: 30
remind_interval: 15m
google:
enabled: true
credentials_file: path/to/oauth-client.json
calendar_id: primary
sync_interval: 5mOr via CLI:
config edit google_calendar true
config edit google_credentials C:\path\to\oauth-client.json
4. Authorize and sync
calendar connect google # browser OAuth; token saved under data/
calendar sync # force pull + push
calendar status # shows Google connection + last sync
Background sync runs every calendar.google.sync_interval (default 5m) while TeleLink is running.
Without Google: local scheduling works fully on its own β no Google account or API keys required.
See how calendar or help calendar (in the shell) for the full topic guide.
API
| Endpoint | Purpose |
|---|---|
GET / |
Upload form (baby-blue web UI) |
POST /intake |
multipart, JSON, or urlencoded |
GET /health |
{"ok": true} |
Optional auth: header X-Telelink-Token or Authorization: Bearer <token> when intake.token is set in config.
JSON fields: sender, message / text, link / url, image_base64 / image, filename
If TeleLink is useful to you, consider buying me a coffee.
MIT