memorysync-cli
v1.6.0
Published
The MemorySync CLI. Add, search and manage agent memory from your terminal, with a JSON mode built for AI agents.
Maintainers
Readme
memorysync-cli
Agent memory from your terminal.
npm install -g memorysync-cli
memorysync init
memorysync add "I prefer TypeScript over JavaScript" --user alice
memorysync search "language preference" --user aliceNo install needed to try it:
npx memorysync-cli helpmemorysync: command not found after installing
The package is fine; your shell cannot see npm's global bin directory. Common on
Windows, and on any machine where a Conda or shell profile rebuilds PATH.
# where npm puts global commands
npm config get prefix
# Windows PowerShell, then reopen the terminal
[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH','User') + ';' + (npm config get prefix), 'User')
# macOS / Linux
echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.zshrcReopen the terminal afterwards. Or skip PATH entirely and use
npx memorysync-cli <command> — every example below works that way too.
Why it looks like this
Zero dependencies. The whole CLI is 19 files and 70 kB packed. Nothing is
pulled from the registry at install time, so there is no transitive tree to audit
and npx is instant.
Every command supports every output format. text, json, table, yaml,
quiet. No command refuses a format that another accepts.
Exit codes tell you what went wrong, so a script can branch without parsing stderr:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Unclassified failure |
| 2 | Usage: unknown command, bad flag, missing argument |
| 3 | Authentication: missing, wrong, expired or revoked key |
| 4 | Plan limit reached. Retrying will not help until the cycle resets |
| 5 | Network: unreachable or timed out. Retrying may help |
| 6 | Not found |
| 130 | Interrupted |
Agent mode
Put --json (or --agent) before the command. Every command then answers with
one envelope, on stdout, whether it succeeded or failed:
memorysync --json search "preferences" --user alice{
"status": "success",
"command": "search",
"duration_ms": 134,
"scope": { "user": "alice", "profile": "default" },
"count": 2,
"data": [
{ "id": "m_60632", "text": "Prefers TypeScript", "score": 0.97 }
],
"quota": { "metric": "retrieval_requests", "used": 12, "limit": 1000, "exhausted": false }
}Results are always an array under data, for every command, so .data[] works
everywhere. Failures use the same envelope with status: "error" and a non-zero
exit, so one code path handles both.
memorysync help --json returns the entire command tree, so an agent can discover
the surface for itself rather than being told about it.
The quota block
Billable commands carry current usage. This exists because the API degrades
silently when a plan limit is reached: add returns success having stored
nothing, and search returns an empty list. That is deliberate, so an assistant
never repeats billing state to an end user, but it means an empty result is
ambiguous. exhausted: true is how you tell the difference, and exit code 4 is
how a script does.
Commands
| | |
|---|---|
| init | Store a credential, pick a default user and project |
| add | Store a fact. Reads stdin, so it composes with pipes |
| search | Natural-language search |
| list | Recent memories for a user |
| get | One memory by id |
| delete | Delete memories. Previews unless you pass --yes |
| import | Bulk load JSON or JSONL, validated before anything is sent |
| export | Write out as json, jsonl or csv |
| quota | Plan usage and when it resets |
| status | Credential, API reachability, active scope |
| doctor | Diagnose setup and say what to fix |
| whoami | Identity and scope in use |
| project | List and select projects |
| source | Inspect and control connected knowledge sources |
| event | Track asynchronous ingestion, with wait |
| config | Local configuration and profiles |
| mcp | Connect MemorySync MCP to your AI clients |
| completion | bash, zsh, fish, PowerShell |
| help, version | |
Run memorysync help <command> for flags and examples.
Credentials
Never written to the config file in plain text. Three tiers, in order:
MEMORYSYNC_API_KEYin the environment. Nothing is stored. Use this in CI.- The OS keychain, through the tool the OS already ships —
securityon macOS,secret-toolon Linux where libsecret is installed. - Otherwise a
0600file, encrypted at rest with a key derived from this machine and user.
Being straight about tier 3, which is where Windows lands, because Windows has no built-in command-line access to Credential Manager: encryption at rest means the file is inert if copied elsewhere or read by another account, so a key cannot be lifted from a synced folder, a backup or a shared screen. It does not stop someone already running code as you.
memorysync doctor reports which tier is in use.
Profiles
For more than one environment:
memorysync init --profile staging --api-key ms_live_xxx --user alice
memorysync config profiles
memorysync config use-profile staging
memorysync --profile production statusPrecedence, highest first: explicit flags, environment variables, the profile, then defaults. Environment beats the profile so a CI image cannot be steered by a config file baked into it.
Environment variables
| | |
|---|---|
| MEMORYSYNC_API_KEY | Credential. Overrides stored keys |
| MEMORYSYNC_BASE_URL | API base URL |
| MEMORYSYNC_USER_ID | Default end user |
| MEMORYSYNC_PROJECT_ID | Default project |
| MEMORYSYNC_PROFILE | Profile to use |
| MEMORYSYNC_OUTPUT | Default output format |
| MEMORYSYNC_TIMEOUT | Per-request timeout, milliseconds |
| MEMORYSYNC_CONFIG_DIR | Config location. Default ~/.memorysync |
| NO_COLOR | Disable colour |
Two things worth knowing
Memory is always scoped to a user. The API refuses key-authenticated memory
calls without one, because storing everything under the key owner would mix your
customers together. Pass --user, or set a default during init. The CLI checks
locally and tells you which flag to add rather than forwarding a 400.
Deleting previews by default. memorysync delete <id> shows what would go and
exits without changing anything; --yes performs it. The preview comes from the
server's own dry run, so what is listed is exactly what a confirmed run removes.
--all is a hard erasure and says so before you confirm.
