md2gd client reference
Everything the md2gd command-line client does: install, sign in, link a Markdown file to a Google Doc, and keep them two-way synced.
Overview
md2gd is a single, dependency-free binary that keeps a local Markdown file and a Google Doc in sync, both directions, block by block. Your agents and tools write Markdown; your team reviews and edits in the real Google Doc; md2gd reconciles the two continuously.
Auth is brokered by the md2gd web app, so the client never opens its own Google screen. Document content flows directly between your machine and Google. It never passes through md2gd’s servers.
md2gd login, then md2gd add. add starts syncing for you; everything below is reference detail.Install
One line. It detects your OS/arch, verifies the checksum, and installs the binary:
curl -fsSL https://md2gd.com/install.sh | shBy default it lands in /usr/local/bin (falling back to ~/.local/bin); set MD2GD_INSTALL_DIR to choose. For a direct download or Windows, see the download page for per-platform binaries and PATH setup.
Confirm it’s installed:
md2gd versionSign in
Connect your Google account. This opens your browser to md2gd.com; you approve once there and the client receives a device token. If you’re already signed in to md2gd.com, it’s instant.
md2gd loginThe device token is saved to credentials.json (permissions 0600) under your config dir: ~/Library/Application Support/md2gd on macOS, ~/.config/md2gd on Linux. Additional machines run the same command and never see a Google screen.
Link a file to a Doc
md2gd add links one local Markdown file to one Google Doc and keeps them in sync, both directions. You either create a new Doc for the file or point md2gd at one you already have.
drive.file scope), never anything else in your Drive. That is also why there is no “link by URL”: a Doc md2gd didn’t create and you didn’t pick is one it can’t read or write, so use --pick to hand it an existing Doc.Create a new Doc with --create
Makes a fresh Doc that the app owns. --title defaults to the file name:
md2gd add notes.md --create --title "Project notes"Pick an existing Doc with --pick
Opens the Google Picker in your browser. The Doc you select is the only thing md2gd can touch, nothing else in your Drive:
md2gd add notes.md --pickOptional: sync into a tab with --new-tab
By default the file syncs to the Doc body, which is all most setups need. Add --new-tab to sync into a new tab instead, leaving the rest of the Doc untouched, so several files can share one Doc, each in its own tab. It works with either option above, and --tab-title defaults to the file name:
md2gd add notes.md --create --new-tab --tab-title "Spec"md2gd add notes.md --pick --new-tabStart syncing
md2gd add already started syncing. The daemon runs in the background and auto-starts on login, so there’s nothing more to do. To turn it off or back on later, see Turn syncing on/off.
Turn syncing on/off
md2gd runs as a per-user background service (macOS LaunchAgent, Linux systemd user unit, Windows logon service) that auto-starts on login and restarts if it crashes. md2gd add enables it automatically and restarts it so new links are picked up right away.
To control it manually: md2gd disable stops and removes the service (your links and login are kept), md2gd enable reinstalls and starts it, and md2gd status reports whether it’s running.
Command reference
| Command | Description |
|---|---|
md2gd login [--api url] | Connect your Google account (browser) and save a device token. |
md2gd logout | Mark the device offline and clear the local token. |
md2gd add <file.md> --create [--title T] | Create a new Doc, link the file to it, and start syncing. |
md2gd add <file.md> --pick | Choose an existing Doc via the browser Google Picker, link, and start syncing. |
md2gd add <file.md> --new-tab [--tab-title T] | Add a new tab to the Doc and sync the file into it. Combine with --create or --pick. |
md2gd ls | List all linked files with their sync status and Doc URL. |
md2gd rm <file.md | sync-id> | Unlink a file. The local file and Google Doc are untouched. |
md2gd resolve [file.md] | Walk any set-aside conflicts (or list an unsupported file’s offenders). --keep-doc-all / --keep-mine-all skip the prompts. |
md2gd enable | Install and start the background service; auto-start on login. |
md2gd disable | Stop and remove the background service. Links and login are kept. |
md2gd status | Print whether the background service is running. |
md2gd token | Vend a short-lived Google access token (diagnostic). |
md2gd version | Print the client version. |
Manage links (ls / rm)
List your linked files and their current sync status:
md2gd lsPrints a table with columns FILE, STATUS, and DOC (the Google Doc URL).
To unlink a file (the local Markdown file and the Google Doc stay untouched, only the sync linkage is removed):
md2gd rm notes.mdThe argument can be a file path, a unique basename, or the sync ID shown by md2gd ls. If it matches more than one link, pass the full path or the exact sync ID.
How sync works
- Two-way, block by block. md2gd diffs at the block level (headings, paragraphs, list items, code blocks) so edits on either side merge without clobbering the whole document.
- Local edits push instantly. The daemon watches your files; saving a linked
.mdreconciles it right away. - Doc edits pull on a periodic poll. Remote (Google-side) changes are picked up on the next poll, about every minute.
- Conflicts: the Doc wins, nothing is lost. If the same block changed on both sides since the last sync, the Doc’s version goes live in your file and yours is set aside in an append-only
<file>.conflicts.mdbeside it, written as a standard diff3 section any editor renders as a three-way merge. Sync keeps flowing (statusclash_resolved); runmd2gd resolveto walk each set-aside block and keep the Doc’s version, restore yours, or hand-merge. - Unsupported constructs stop that file. Content md2gd can’t round-trip losslessly (for example images or horizontal rules) halts that file’s sync with a clear status instead of corrupting the document. Remove the construct and sync resumes on its own.
- Tokens refresh themselves. The client holds only short-lived (~1h) Google access tokens and re-vends them before expiry. On a transient Google error it retries with backoff (up to ~5 min) before reporting the link as errored; a 401 triggers an immediate token refresh and retry.
Config & environment
| Variable | Purpose |
|---|---|
MD2GD_CONFIG_HOME | Directory for credentials.json (defaults to your OS config dir). |
MD2GD_INSTALL_DIR | Install location for the curl | sh installer. |
Credentials live in credentials.json (0600): the device token, the API base, and a stable device ID that a re-login reuses so you don’t pile up duplicate devices.
Diagnostics
Check the service is up:
md2gd statusSign out
Marks this device offline on the dashboard and clears the local token. Your links and the stable device ID are kept, so a later md2gd login reconnects the same device:
md2gd logoutTroubleshooting
“Unidentified developer” on first run (macOS)
The binary isn’t notarized yet. Clear the quarantine flag once:
xattr -d com.apple.quarantine $(which md2gd)A link shows “auth expired”
Rare, since refresh tokens are long-lived. It happens only if you revoked access in Google settings, went ~6 months without syncing, or changed your Google password. Fix with md2gd login.
A link shows an error or “broken”
The Doc may have been deleted, or access to it removed. Re-link with md2gd add, or remove the link from the dashboard.
A file stopped syncing with “unsupported”
That file contains a construct md2gd won’t round-trip (for example an image or a horizontal rule). Run md2gd resolve <file.md> to list exactly what to change, on each side; remove or adjust it and the file resumes on the next reconcile.