Local-first Markdown memo CLI backed by Git and webhooks.
memoscli is a small command-line memo tool for people who prefer writing from the terminal. It is not a replacement for the Memos web app. The core idea is simpler: one memo is one Markdown file, Git records history and sync, and optional webhooks notify external systems.
- Local-first storage in Markdown files
- One memo per file under
~/.memo/memos/YYYY/MM/DD/ - Git commit for each add/edit/delete
- Optional background Git sync
- Safe edit mode that edits only the memo body
rgsearch withgrepfallbackfzfselection with a Node prompt fallback- Webhook event queue with retry
- Node.js 20+
- Git
- A POSIX-like shell for background Git sync
Optional:
- pnpm for source development
rgfor faster searchfzffor interactive selectionnvim,vim,vi, ornanoif$EDITORis not set
Fully supported:
- macOS
- Linux
Supported via compatibility layer:
- Windows with WSL or Git Bash
Not guaranteed in v0.1:
- Windows native PowerShell/CMD
Some features, especially background Git sync and command detection, currently depend on sh.
npm i -g memoscli
memo --helpThe npm package is named memoscli; the installed command is memo.
You can also try it without installing globally:
npx memoscli --helpmemo init
memo a "A local-first memo idea #idea #cli"
memo ls
memo s "local-first"To use the same memo repository on another computer, initialize from the existing GitHub repository:
memo init --from https://github.com/user/memos.gitOr choose a custom local data directory:
memo init ~/notes/memo --from git@github.com:user/memos.gitOpen a memo:
memo sh 20260505143012-a8f3Edit a memo:
memo e 20260505143012-a8f3Delete a memo:
memo rm 20260505143012-a8f3Manual Git sync:
memo sygit clone https://github.com/imfangli/memoscli.git
cd memoscli
pnpm install
pnpm build
pnpm link --global
memo --helpIf pnpm link --global reports that the global bin directory is missing:
pnpm setup
source ~/.zshrc
pnpm link --globalDevelopment mode:
pnpm dev --help
pnpm dev init /tmp/memo-demoDefault data directory:
~/.memo/
├── memos/
├── assets/
├── events/
│ ├── pending/
│ ├── sent/
│ └── failed/
├── config.toml
└── .git/
Memo file example:
---
id: 20260505143012-a8f3
created_at: '2026-05-05T14:30:12+08:00'
updated_at: '2026-05-05T14:30:12+08:00'
tags:
- idea
- cli
---
Today I thought about a local-first memo tool.
#idea #cliconfig.toml is ignored by the memo data repository because it may contain local paths, webhook URLs, or secrets.
Open:
$EDITOR ~/.memo/config.tomlDefault shape:
data_dir = "/Users/you/.memo"
[git]
auto_commit = true
auto_push = false
default_branch = "main"
[editor]
raw_by_default = false
extract_tags_from_body = true
[webhook]
enabled = true
auto_send = true
timeout_seconds = 5
retry = 3
secret = ""You can override the data directory per command:
memo --data-dir ~/notes/memo a "Write somewhere else"Or by environment variable:
MEMO_DIR=~/notes/memo memo lsEach memo mutation creates a local Git commit.
On the first computer, initialize a data directory, add an origin remote, then sync:
memo init
git -C ~/.memo remote add origin git@github.com:user/memos.git
memo syncOn another computer, clone that data repository and finish local setup in one step:
memo init --from git@github.com:user/memos.git
memo syncconfig.toml is created per machine and is not synced because it can contain local paths, webhook URLs, and secrets.
Manual sync:
memo syncEnable background sync after add/edit/delete:
[git]
auto_push = trueWhen enabled, mutation commands return immediately after starting background sync:
Git: sync started in background. Log: .git/memo-sync.log
Check the log inside the memo data repository:
tail -f ~/.memo/.git/memo-sync.logConfigure endpoints in ~/.memo/config.toml:
[webhook]
enabled = true
auto_send = true
timeout_seconds = 5
retry = 3
secret = ""
[[webhook.endpoints]]
name = "n8n"
url = "https://n8n.example.com/webhook/memos"
enabled = true
events = ["memo.created", "memo.updated", "memo.deleted"]Commands:
memo wh st # status
memo wh f # flush pending events
memo wh r # retry failed events
memo wh t n8n # send test eventIf webhook.secret is set, requests include:
x-memo-timestamp
x-memo-signature: sha256=<hmac>
Webhook queue files are runtime state and are ignored by Git.
Search:
memo s "webhook"
memo s "webhook" --matches
memo s "webhook" --pathmemo uses rg when available and falls back to grep.
Search output highlights matches unless NO_COLOR is set:
NO_COLOR=1 memo s "webhook"Interactive selection uses fzf when available:
memo o
memo e --select
memo rm --select
memo s "webhook" --select| Command | Alias |
|---|---|
memo init |
memo i |
memo add |
memo a |
memo list |
memo ls |
memo today |
memo td |
memo show |
memo sh |
memo open |
memo o |
memo edit |
memo e |
memo delete |
memo rm |
memo search |
memo s |
memo sync |
memo sy |
memo status |
memo st |
memo webhook |
memo wh |
Webhook aliases:
| Command | Alias |
|---|---|
memo wh status |
memo wh st |
memo wh flush |
memo wh f |
memo wh retry |
memo wh r |
memo wh test |
memo wh t |
memo wh generate |
memo wh g |
Check your global npm bin path:
npm bin -g
npm config get prefixMake sure the global bin directory is in your PATH.
Run:
pnpm setup
source ~/.zshrc
pnpm link --globalUse:
memo syncmemo sync can set upstream automatically when an origin remote exists.
Choose an empty directory, remove the old directory yourself, or clone into a different path:
memo init ~/notes/memo --from git@github.com:user/memos.gitmemo will not overwrite files in a non-empty target directory.
Check that the repository URL is correct and that Git can authenticate:
git ls-remote git@github.com:user/memos.gitFor SSH URLs, make sure your SSH key is available to Git. For HTTPS URLs, make sure your Git credential helper has a valid token.
Make sure the remote is the memo data repository, not the memoscli source code repository. A memo data repository should contain the memos/ directory and Git history for your memo files.
Check:
tail -n 80 ~/.memo/.git/memo-sync.log
git -C ~/.memo status --short --branchmemo still works. Search falls back to grep, and interactive selection falls back to a basic Node prompt.
See CONTRIBUTING.md.
This package is published to npm from GitHub Releases via npm Trusted Publishing.
Trusted publisher settings on npm:
- Package:
memoscli - Publisher: GitHub Actions
- Owner:
imfangli - Repository:
memoscli - Workflow filename:
publish.yml
Release tags must match package.json exactly, for example v0.1.0.
See SECURITY.md.
MIT