Skip to content

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

memoq

A slim, non-interactive command-line client for a personal Memos server, built to be driven by a coding agent. No TUI, no MCP server, no embedded LLM — just a scriptable CLI over a local SQLite cache (FTS5 full-text search) with read-time lazy sync, plus full coverage of the Memos v1 REST API.

Every read command supports --json; nothing ever prompts for input.

Memos compatibility

As of 2026-08-13, the latest stable Memos release is v0.30.0.

Memos version Status in memoq
v0.30.0 Stable compatibility baseline; attachment and object-based relation requests covered by contract tests

These are deterministic HTTP contract tests using local mock servers, not a live Memos end-to-end test matrix. The generic memoq api command and raw resource commands may work with other Memos v1 releases, but they are not part of the stated compatibility baseline.

Install

Requires Go 1.24+. Build a single self-contained binary (~15 MB, pure Go, no CGO), then put it on your PATH:

make build
mv bin/memoq ~/bin/        # any directory on your PATH

Configure (one-time)

Point memoq at your server and personal access token:

memoq config set server_url https://memos.example.com
memoq config set token <personal-access-token>
memoq config set auto_sync_ttl_seconds 30   # optional; 0 disables read-time auto-sync
  • memoq config list — dump the current config as JSON (the token is masked).
  • memoq config get <key> — read one key (server_url / token / auto_sync_ttl_seconds).
  • memoq config path — print the config file and DB file locations.

Data lives under $MEMOQ_HOME (default ~/.memoq). The token is stored 0600 and is never echoed back (config get token → ***set***).

Usage

memoq has two command tiers. Use the fast local-cache commands for everyday search/list/read/write; drop to the full-API resource commands for anything the cache doesn't model.

Fast local-cache commands (FTS5 + read-time lazy sync)

These serve a local SQLite cache and auto-sync from the server if it is staler than auto_sync_ttl_seconds. Add --no-sync to skip the sync and use the cache as-is; add --json to parse the output. Flags may appear before, after, or between positional args.

# search (full-text, FTS5 with a LIKE substring fallback)
memoq search "deployment checklist" --json --limit 10
memoq search "同步" --json          # CJK: use a short substring, not a full sentence

# list with filters (pinned-first, then newest-first)
memoq list --json --limit 50
memoq list --tag project --from 2026-01-01 --to 2026-06-30 --json
memoq list --visibility PUBLIC --offset 50 --json

# read one note
memoq get <uid> --json

# create (body from --content or stdin; --tag / --attach are repeatable; --visibility defaults to PRIVATE)
memoq create --content "Ship notes: cut RC on Friday" --tag release --tag ops
echo "multi-line body" | memoq create --tag inbox
memoq create --content "Design review" --attach ./diagram.png --attach ./notes.pdf

# update / delete
memoq update <uid> --content "revised body" --visibility PROTECTED
memoq delete <uid> --dry-run --json
memoq delete <uid> --yes --json

# overview + tag counts (handy to discover which tags exist before list --tag)
memoq stats --json

# force a full incremental sync from the server
memoq sync --json

Notes:

  • Freshness: reads auto-sync per the TTL. If you used --no-sync, or a get misses a just-created note, run memoq sync and retry.
  • CJK search: FTS5 tokenizes Chinese poorly, so the LIKE fallback carries it — search with a concrete substring rather than a full sentence. If a search misses, fall back to list --tag / date filters.
  • No matches is normal, not an error — retry with different keywords.
  • An auto-sync skipped (...) line on stderr is non-fatal: the network/auth failed but the local cache is still served. Only a non-zero exit is a real failure.
  • create and update are high-frequency operations and run directly. Add --dry-run only when you want to preview them.
  • Destructive operations never prompt. Preview them with --dry-run, obtain confirmation, then execute with --yes.
  • create, update, and delete support --json and return a consistent result containing operation, uid, and cache_status.

Full-API resource commands (direct to server, raw JSON out)

Modeled on lark-cli's resource-group + verb structure. These hit the Memos v1 REST API directly and print the pretty-printed JSON response, giving complete API coverage independent of the local cache.

Group Verbs
memo list get create update delete comments comment relations relate set-relations reactions react unreact attachments set-attachments shares share unshare link-metadata
attachment list get create update delete batch-delete pull
user list get create update delete all-stats stats settings setting update-setting tokens create-token delete-token webhooks
auth me signin signout refresh
shortcut list get create update delete
instance profile setting update-setting stats
ai transcribe
api <METHOD> <PATH> — generic escape hatch covering any endpoint

Supply request bodies / query params with:

Flag Meaning
--body '{...}' Raw JSON request body
--body-file <path|-> JSON body from a file (- = stdin)
--field k=v Body field, repeatable (value is JSON-parsed, string fallback)
--query k=v Query parameter, repeatable
--dry-run Print the planned request without sending it
--yes Confirm a destructive operation
--sync Sync the local memo cache after a successful request
--json Accepted for consistency; resource commands always emit JSON

attachment create additionally accepts --file <path>. memo set-attachments accepts repeatable --attachment attachments/<id> values.

# typed resource verbs
memoq memo list --query pageSize=10 --query state=NORMAL
memoq memo create --field content='hi' --field visibility=PRIVATE
memoq memo update <uid> --field pinned=true --query updateMask=pinned
memoq memo delete <uid> --dry-run
memoq memo delete <uid> --yes
memoq memo relate <uid> <related-uid>
memoq memo attachments <uid> --json
memoq memo set-attachments <uid> --attachment attachments/<id>
memoq attachment create --file ./document.pdf
memoq user get me
memoq instance profile

# generic escape hatch — reaches any endpoint, current or future
memoq api GET  /api/v1/memos --query pageSize=10
memoq api POST /api/v1/memos --field content='hello' --field visibility=PRIVATE
memoq api PATCH /api/v1/memos/<uid> --body '{"pinned":true}' --query updateMask=pinned --sync

Run any group with no verb (e.g. memoq user) to print its verb list.

memo create, memo update, and memo delete sync the local memo cache after success. Other resource commands leave it unchanged. Generic api calls only sync when --sync is supplied. A cache-sync failure after a successful remote write is reported on stderr without converting the completed write into a command failure; run memoq sync to repair the stale cache.

memo relate adds a REFERENCE while preserving existing relations. It uses the object-based relation format from the stable Memos v0.30.0 API. memo set-relations remains available for full replacement.

Safe automation

memoq remains fully non-interactive. create and update execute immediately because they are frequent and recoverable. Destructive or high-impact commands such as delete, batch-delete, share/unshare, token deletion, and user/instance setting changes require --yes.

# Preview: no network request and no local mutation.
memoq delete <uid> --dry-run --json

# Execute only after the user approves the exact preview.
memoq delete <uid> --yes --json

Resource commands always produce JSON, including structured JSON errors. Fast commands produce structured errors on stderr when --json is present.

HTTP debugging

Set MEMOQ_HTTP_DEBUG to inspect requests made through the req client. Debug output goes to stderr, so JSON stdout stays parseable.

MEMOQ_HTTP_DEBUG=headers memoq memo list --query pageSize=10
MEMOQ_HTTP_DEBUG=trace memoq sync --json
MEMOQ_HTTP_DEBUG=dump memoq api GET /api/v1/auth/me

Supported modes: headers, dump, trace, debug, dev. dump / dev may expose tokens or memo content; attachment downloads do not dump response body.

Attachments (local download for agents)

Attachment blob bytes are never returned by the JSON API — the Memos proto marks the attachment content field INPUT_ONLY, so attachment list / memo attachments <uid> only print metadata (name / filename / type / size). The raw bytes are served by a separate file-server route, /file/attachments/{id}/{filename}.

To let an agent actually read an image/file, use attachment pull:

memoq attachment pull <memo-uid>          # download all of a memo's attachments
memoq attachment pull <memo-uid> --json   # machine output with local_path per file
memoq attachment pull <memo-uid> --force  # re-download even if a local copy exists

It downloads each blob to $MEMOQ_HOME/attachments/<id>/<filename>, records the metadata + absolute local path in the SQLite cache, and prints where each file landed (+ downloaded, = already present, - external link not downloaded). With --json, parse the local_path field and Read that path directly. The bearer token is attached to the download so PRIVATE/PROTECTED attachments work; externally-linked attachments are recorded but not downloaded.

memoq create --attach <path> uploads a local file and associates it directly with the newly created memo; repeat --attach for multiple files. Memos receives attachment bytes as JSON base64, not multipart upload. Large files remain subject to the instance upload limit (32 MiB by default when the server has no configured limit). If an attachment upload fails after the memo is created, the command reports the created memo UID and does not delete it.

To upload independently, use memoq attachment create --file <path>. Associate the result with an existing memo using repeatable memoq memo set-attachments <uid> --attachment attachments/<id>. This replaces the memo's complete attachment set; clear it explicitly with --body '{"attachments":[]}'.

Layout

main.go                     entry point → cli.Run
internal/config/            config + path resolution
internal/store/             SQLite store, FTS5, migrations, attachment cache
internal/memos/             Memos v1 API client (generic Do + typed helpers)
internal/syncer/            incremental sync engine
internal/cli/               command dispatch, output formatting
internal/cli/resource.go    lark-cli-style resource-group + verb commands (full API)
internal/cli/attachments.go attachment blob download (attachment pull)
skills/memoq/SKILL.md       companion Skill for coding agents
docs/ENDPOINTS.md           canonical endpoint manifest (diff target)
docs/UPSTREAM_SYNC.md       playbook for tracking upstream Memos API changes

Development

go build ./... && go vet ./... && gofmt -l . && go test ./...

HTTP requests use github.com/imroc/req/v3; SQLite uses the pure-Go modernc.org/sqlite driver.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages