Integrations

Memtro CLI

A coding agent in your terminal: it reads, edits and runs commands in your repository with your company's memory behind it, asks before anything risky, and runs one task for a script with memtro run.

Open Memtro

memtro is a coding agent in your terminal, with your company's memory and connected systems behind it. You say what you want; the agent reads the code, edits files and runs commands in the directory you started it in, on your computer, and asks you before anything it is not sure of. It can also search what your organisation knows (decisions, tickets, conversations) and, if you allow it, act in the systems your organisation has connected to Memtro. The model runs on Memtro; the tools run on your machine.

curl -fsSL https://raw.githubusercontent.com/memtro-org/memtro-cli/main/install.sh | sh
cd your-repo
memtro

It is open source at memtro-org/memtro-cli. Read Safety before you use it on anything you care about: there is no sandbox.

Install

The install script puts one standalone executable, memtro, in ~/.memtro/bin. It needs no Node.js; Node.js is needed only to build from source. It runs on macOS (Apple silicon and Intel) and on Linux with glibc (x86_64 and arm64). Windows is supported through WSL: run the script inside WSL. No native Windows build is released, and the script refuses to run natively on Windows. Alpine and other musl-based Linux are not supported yet: the script says so and stops.

What the script does, in order:

  1. It works out your system and processor. An Intel shell on Apple silicon still gets the Apple silicon build.
  2. It downloads the build for your system, and the release's checksums.txt, from the same GitHub release, over HTTPS only.
  3. It checks the download's SHA-256 against checksums.txt. If the sums differ, or if neither sha256sum nor shasum is installed, it stops and installs nothing.
  4. It runs the new memtro --version to prove it starts, then moves it into place in one step. An install that fails partway leaves any earlier one as it was.

It never uses sudo and never asks a question. It changes nothing outside the install directory, so your shell's startup file is left alone; if the directory is not on your PATH, it prints the line to add for your shell.

VariableEffect
MEMTRO_VERSION=0.2.1Install this version (0.2.1 or v0.2.1) instead of the latest.
MEMTRO_INSTALL_DIR=/path/binInstall here instead of ~/.memtro/bin.
MEMTRO_MODIFY_PATH=1Add the directory to PATH in your shell's startup file. It is added once, and the script says which file it changed.
curl -fsSL https://raw.githubusercontent.com/memtro-org/memtro-cli/main/install.sh | MEMTRO_VERSION=0.2.1 MEMTRO_MODIFY_PATH=1 sh

To read the script before you run it, download it first with curl -fsSL -o install.sh https://raw.githubusercontent.com/memtro-org/memtro-cli/main/install.sh. The script checks the download's SHA-256 only, not its build attestation. Each release's builds also carry a GitHub build-provenance attestation, which memtro update checks for itself (see Updating) and which you can check by hand with the GitHub CLI signed in: gh attestation verify memtro-darwin-arm64.tar.gz -R memtro-org/memtro-cli.

On macOS the first start after an install or update takes a second or two while the system checks the new file.

First run and signing in

There is no login command. The first time you run memtro (or any command that needs a key) in a terminal with no key stored, it says which Memtro it is signing you in to, prints the address and opens your browser on Memtro's approval page. The page shows who you are signed in as, which organisation the key will belong to (you choose, if you are in several) and the name of this computer. Approve, and the terminal says:

Signed in as Ada Lovelace <ada@example.com> in Analytical Engines; this key expires on 10 Feb 2027 (in 90 days).

and the command carries on. The sign-in lines go to stderr, so memtro whoami --json signs in and still prints only JSON on stdout.

The key belongs to that organisation and acts as you there: it reads and writes the organisation's memory as you, with your connections and your permissions, and nothing more. It appears on Dashboard → Profile → API keys as Memtro CLI (<this computer>), where you can revoke it like any other key.

  • It expires after 90 days. The next session you start in a terminal says the key was refused or has expired, signs you in again once, and carries on. The new key is stored only after that sign-in succeeds, and the old one is then revoked.
  • To sign out, run memtro logout, or type /logout in a session. It revokes the key on Memtro and removes it from this computer; the next command signs you in again.
  • To sign in as somebody else without leaving a session, type /login. The conversation carries on with the new key.
  • To check the key, run memtro whoami. It asks Memtro rather than reading the local copy, so a revoked key says so.

The address is always printed before the browser is asked to open it, so a browser that opened on another desktop, or not at all, is not a dead end.

Over SSH, or anywhere without a browser, add --no-browser to whatever you are running:

memtro --no-browser

It prints the address. Open it on any device, approve, and paste the code the page shows into the waiting terminal. The CLI chooses this by itself over SSH and on Linux with no display. The code works once, for two minutes, and only together with a secret the terminal never sent anywhere, so a code somebody else sees is no use to them.

Nobody at the terminal. Signing in needs a person: stdin and stdout must both be terminals and CI must not be set. Piped, redirected, run by another program or in CI, a command with no key, or with a refused one, exits 3 with Not signed in. Run `memtro` in a terminal to sign in, or set MEMTRO_API_KEY. and opens nothing. Use MEMTRO_API_KEY there (see CI).

For anybody reviewing it: this is the OAuth 2.0 authorization code flow with PKCE (RFC 7636) and a loopback redirect (RFC 8252). The CLI listens on 127.0.0.1 on a random port, answers only GET /callback, checks state in constant time, and gives up after five minutes. No password and no browser cookie ever reaches it.

A session

memtro opens a session in the current directory. In a terminal of at least 50×12 it takes over the whole window, as vim or less do, and gives it back as it was when you leave, with one line saying the session's id and how to resume it (a session with nothing said in it is not saved). It starts on a screen with the wordmark, any notice such as an available update, and the input. Then the conversation fills the screen, newest at the bottom, under a header with the session's title, the tokens used and how much of the model's context is in use. The input stays at the bottom, with a status line under it showing the mode, the model in use, your organisation and how much of the context is in use. You type what you want:

> the cart total is wrong when a discount code and free shipping both apply; find out why and fix it

With --inline, "ui": "inline" in config.json, in a smaller terminal, with TERM=dumb, or when stdin or stdout is not a terminal, you get the inline interface instead: output scrolls like any other command's, and the terminal's own scrollback and selection work as usual. Everything below about commands and being asked applies to both; the menus and dialogs, the approval panel, the themes and the mouse belong to the full-screen interface. Inline, one line after each turn says which model answered, the tokens the turn used, how much of the context window is in use, and the mode.

The agent works with these tools on your computer:

ToolWhat it does
read, list, glob, grepRead files, list directories, find files by pattern, search their contents.
edit, writeChange a file, or create one.
bashRun a command in your shell, in the session's directory.

and Memtro's own tools beside them: searching your organisation's memory, and one tool per connected system. Those run on Memtro, with your key's connections and permissions. With actions off (the default) Memtro offers only the tools that read. /actions on, or --actions when you start, lets them change things in connected systems; each such call is still checked (see Safety), and still runs only once an administrator has granted that action by name, as everywhere in Memtro.

Project instructions. AGENTS.md and CLAUDE.md are read from each directory between the repository's top level and where you started, outermost first, so a nearer file can refine an outer one. Each file is capped at 32 KB.

Near the model's context limit, when the limit is known, the older part of the conversation is summarised on its own; /compact does it now.

The conversation

Answers are rendered as Markdown: headings, lists, tables, code with highlighting, and links with their address shown in full after the text. Clicking the text or the address opens that address, except where the text itself reads as a different address or host, such as [paypal.com](https://elsewhere.example) or "log in at paypal.com": that text opens nothing, and only the address shown after it does.

Each tool call is one line with its outcome folded in (✓, ✗ exit 1, ✗ refused: …). An edit shows its diff under it, and a command shows the last lines of its output while it runs and when it fails. The model's thinking shows as one line ("thought for N lines") unless you started with --verbose. After each answer a muted line says which model answered and the tokens the turn used.

Keys

KeyWhat it does
EnterSend. Alt+Enter or Ctrl+J starts a new line, as does Shift+Enter in a terminal that reports it, and a line ending in \.
Up / DownMove through earlier messages (this directory's history), or through a menu when one is open.
Esc or Ctrl+C while it worksStop the turn. Every tool call it had asked for gets a result saying so, and the session carries on. Ctrl+C twice within two seconds leaves.
Enter while it worksQueue the message: the hint row says "queued" and it is sent when the turn ends. If you stop the turn with Esc instead, the queued message goes back into the input.
Ctrl+C at the promptClear what you typed; at an empty prompt, twice within two seconds leaves. Ctrl+D at an empty prompt leaves too.
Ctrl+PThe command palette: every slash command and key, grouped, filtered as you type.
PageUp / PageDown, Ctrl+Alt+U / DScroll the conversation a page, or half a page. While you are scrolled up, new output does not move the view, and a "↓ newer" marker shows.
Ctrl+G / Ctrl+Alt+GTop / newest. Home and End do the same when the input is empty.
Ctrl+LRepaint the whole screen.
Ctrl+ZSuspend; fg brings it back as it was.

Pasting. Pasted text keeps its lines, and a long paste shows as [pasted N lines] until it is sent. A paste never sends, runs a command, chooses in a menu or dialog, or sends an approval note, even in a terminal that does not mark pastes: there, an Enter that arrives together with other typed text, or within 30 ms of it, starts a new line instead. On a slow connection that can join what you typed and your Enter into one delivery, so the message gets a new line rather than being sent; press Enter again and it is sent, without that extra line. Both interfaces follow this rule.

Mouse. The wheel scrolls the conversation. While memtro has the mouse, your terminal's ordinary click-and-drag selection needs a modifier: hold Shift while you drag (Option in iTerm2). --no-mouse, or "mouse": false in config.json, leaves the mouse to the terminal: plain selection, and no wheel scrolling.

Colours. memtro has a dark and a light theme of its own, chosen by "theme" in config.json ("auto", "dark" or "light"), --theme, or /theme. auto, the default, follows the terminal's background when the terminal says what it is, and is dark otherwise. Colours drop to 256 or 16 when the terminal has fewer, and to none with NO_COLOR or --no-color, where the selected row is also marked with >.

Slash commands

Typing / as the first character opens a menu of commands above the input, narrowed as you type. Up and Down (or Ctrl+P and Ctrl+N) move through it.

  • Tab completes the selected name into the input and never runs anything.
  • Enter runs it. A command that takes an argument, such as /model, is completed first unless what you typed already names it. A command that acts, such as /logout, /new or /update, runs only when what you typed is its name or a beginning no other command shares; otherwise it is completed, and a second Enter runs it.
  • Esc hides the menu and keeps your text.

Once a command's name and a space are typed, the menu lists that argument's choices: the models after /model , ask and auto after /mode , the sessions after /resume , on and off after /actions , and auto, dark and light after /theme . Text of more than one line is always a message, even when it starts with a command's name. To send a message that starts with /, begin it with a space.

Typing @ lists the repository's files (as git or rg count them, .gitignore honoured), narrowed as you type; Tab or Enter puts the path into your message as text.

CommandWhat it does
/model [<id>]Pick from the models your account offers (the current one is marked), or switch to one by id. Under your organisation's routing lock the list says that whatever you pick is answered by the organisation's default model.
/mode ask, /mode autoChange how much you are asked (see Safety). /mode alone says which mode you are in.
/actions on, /actions offLet Memtro's tools change connected systems, or stop them. Off by default.
/newStart a new session; this one stays saved.
/resume [<id>]Pick one of this directory's earlier sessions (title, age, model) to carry on.
/compactSummarise the older part of the conversation now.
/copyCopy the last answer, as the Markdown the model wrote, to the clipboard (below).
/theme [auto|dark|light]Change the colours.
/toolsThe tools the model can use, on this computer and from Memtro; those that can change something are marked.
/loginSign in again, perhaps as somebody else; the conversation carries on. The full screen steps aside while you sign in.
/logoutRevoke this computer's key, remove it, and leave.
/updateUpdate memtro to the latest release; it starts the next time you run memtro.
/whoamiWho the key belongs to.
/helpThe keys and commands.
/quit or /exitLeave (Ctrl+D does too).

/copy leaves out any control characters, and says it did. It goes through your terminal (the OSC 52 clipboard request), so it works over SSH, but memtro cannot see whether the terminal accepted it, and says so. Terminal.app does not accept it, tmux passes it on only with set -g set-clipboard on, and an answer too large for one request is refused rather than cut.

Being asked

When a call needs your approval, the question appears in a panel just above the input, with a yellow bar, so the conversation that led to it stays in view; the input is disabled until you answer. Inline, the session stops and shows it, along these lines:

Allow this?
  Run the cart tests
  $ npm test -- src/cart
  why  the safety check was unsure: …
  y yes, once  ·  a yes to any `npm test …` for this session  ·  n no  ·  e no, and say what to do instead

You see the call's title; the command in full, wrapped but never shortened, with every control character and escape sequence shown rather than obeyed, so a command cannot hide part of itself or redraw the prompt; the diff, for an edit; where a linked path really leads; and why you are being asked and by what. If the panel is taller than the space, Up, Down, PageUp, PageDown or the wheel scroll it, and it says "more above" or "more below". Until its last line has been on screen, y and a allow nothing (it says "read to the end to allow"), and the same holds while the window is too small to show the whole panel. Then one key answers:

  • y: yes, once.
  • a: yes for the rest of the session, only when offered. The prompt says exactly what it would cover, such as "any npm test …".
  • n: no.
  • e: no, with a note the model reads, to say what to do instead. Enter sends the note; Esc goes back to the choices.

Enter is not yes, pasted text does not answer, and keys typed before the prompt appeared are ignored. Typing is not a decision either: a key that follows any other input by less than about a third of a second (the end of a word you were still typing when the prompt appeared) answers nothing, and an answering key takes effect a moment after you press it, unless another key follows at once. Mouse, focus and resize events never answer. Only one question is open at a time. Esc or Ctrl+C refuses (Ctrl+C also stops the turn), and if the turn is stopped while the question is open, the call is refused. Both interfaces follow these rules.

Safety

There is no sandbox. Commands run as you, with your account's permissions, in the directory you started in. A command the agent runs can do anything you could do from that shell.

Every tool call goes through a check before it runs, in this order, and a later step can never loosen an earlier one:

  1. With --unchecked, everything runs and nothing below applies. It prints a warning every time it starts.
  2. Always asked, whatever the mode, whatever you allowed earlier and whatever the safety check thinks: the list below. Allowing one of these is offered once only, never for the session.
  3. Reading files inside the repository (the git top level, or the directory you started in) and reading from Memtro just happen. Not when that "repository" is your home directory or above it: then nothing counts as inside a project, and the session's first lines say so.
  4. Something you allowed for this session runs.
  5. In ask mode, every edit, command and action in a connected system is put to you.
  6. In auto mode (the default), edits inside the repository and a short list of read-only commands run without asking. Everything else (other commands, edits or reads outside the repository, actions in connected systems) goes to the safety check: a separate call to a model, which answers allow, ask or block. Its doubts are put to you; a block is refused, and the model is told why.
  7. If the safety check is off, times out (8 seconds by default; classifier.timeoutMs in config.json changes it, up to a cap of 60 seconds), fails, or answers anything but a clean verdict, the call is put to you. It fails closed to asking, never to running.

Always asked. Running as another user (sudo, su and the like); downloading and running in one command (curl or wget with a shell or interpreter, or anything piped into a shell); a git push that may force or delete, history rewriting, git hooks, and git config that sets anything; anything that prints or touches stored credentials, keychains, private keys, .env files and credential stores such as ~/.ssh, ~/.aws, ~/.kube or memtro's own configuration; printing the environment, or a variable whose name contains key, token, secret, passw, credential or auth; memtro token; and an argument that is a symbolic link into any of those places. For edits and writes it also covers anything inside .git, your shell's startup files and your git configuration. Commands are scanned as text, in several spellings, for these words rather than parsed, so a false alarm costs one question. The README's Safety section lists every word and path.

The read-only list that auto runs without asking anybody, not even the safety check: ls, pwd, cat, head, tail, wc, file, stat, which, echo, rg, grep and find; git status, git diff, git log, git show, git rev-parse, git ls-files and git blame, and listing branches and remotes; and the --version of the usual toolchains. A command qualifies only when it is one line with nothing the shell would expand or redirect, every part of it is on the list, it uses no option that runs a program, writes or follows links (find -exec, rg --pre and the like), and every argument stays inside the repository. In a session whose repository is your home directory or above, nothing is on the list.

What the safety check sees. What you have typed in this session (most recent last, capped), the working directory and whether it is a git repository, the titles of earlier calls this turn, and the pending call: the tool, its title, the command in full, the paths and the diff (capped). It never sees tool results or the model's own words, so a file or a web page cannot argue its case to it. Everything quoted to it is marked as data, and titles, which the agent writes, are marked as the agent's words, not yours. If any part of the pending call had to be cut to fit, an "allow" is turned into a question for you.

It runs on the cheapest model your account lists that it recognises. Under your organisation's routing lock that is the organisation's default model, unless an administrator allows explicit models so a small one can be used. classifier.model in config.json picks one. --no-classifier, or "enabled": false under classifier in config.json, turns it off, and then auto behaves exactly like ask.

Your own rules for a session. --allow-edits lets edits inside the repository through without asking. --allow "npm test" (repeatable) lets a single command starting with those words through. Neither ever covers the always-asked list or chained commands. A prefix covers further arguments only while the command stays one simple command (no ;, &&, pipes, redirection or substitution), its arguments stay inside the repository, and none of them is a NAME=value or an option that runs a program, loads code or configuration, or writes output.

What this does not protect against.

  • It is not a sandbox.
  • Code the agent writes into the repository can then be run by a command you allowed. npm test runs whatever the test script is now.
  • Allowing a Memtro tool for the session covers every operation of that tool.
  • A command that assembles a path at run time can slip past the text rules; it is then judged only by the safety check, or by you.
  • Copying or archiving your whole home directory is not on the always-asked list. Like any other command, it is judged by the safety check, or by you.
  • A symbolic link is resolved when a call is checked. A write whose path has started to lead elsewhere by the time it runs is refused; a command is not checked again.
  • Programs that use stored credentials on their own (ssh, gh, aws, kubectl, git push) are not detected as such.
  • .env files inside the repository are readable without asking.
  • The safety check is shown what you typed in the session, which is how it knows what you asked for, and it is a model: it can be wrong.

Everything the model or a tool prints is cleaned before it reaches your terminal: escape sequences are made visible, so a file's contents cannot move the cursor, set the window title or write to your clipboard. Commands the agent runs get your environment without MEMTRO_API_KEY, MEMTRO_CREDENTIALS, any other MEMTRO_* key, token or secret, and any variable whose value holds a Memtro key.

memtro run, for scripts and agents

For one task with nobody watching:

memtro run "make the failing test in src/cart pass"
memtro -p "summarise what changed in the last 10 commits"
git diff | memtro run "review this"
memtro run --json "…" | jq

The task can come as an argument, after -p, or on stdin. The agent works in the current directory with the same tools and the same checks as a session, but memtro run asks nobody anything: a call that would need your approval is refused, and the model is told which flag would have allowed it. So --allow, --allow-edits and --actions decide how far it can go. The answer goes to stdout and the tool lines to stderr; --json prints every event as one JSON line on stdout instead.

It exits 0 when the agent finished, 1 when it failed or stopped at a limit, 3 when it is not signed in or the key was refused at the start, 4 when Memtro could not be reached, and 130 when interrupted.

Options for a session and for run

OptionWhat it does
--model <id>Which model (default auto; /model lists them).
--mode ask, --mode autoHow much you are asked. auto is the default.
--continueCarry on this directory's most recent session.
--resume [<id>]Carry on a session; without an id, pick from a list.
--actionsLet Memtro's tools change connected systems. Each such call is checked.
--no-classifierNo safety check: everything it would judge is asked instead.
--allow-editsAllow edits to files in this repository without asking.
--allow <prefix>Allow a single command starting with these words without asking. Repeatable.
--uncheckedRun every tool call without checking or asking anything.
--jsonFor run: one JSON event per line on stdout.
--inlineFor a session: the inline interface instead of the full screen.
--theme auto, dark, lightFor a session: the colour theme. auto is the default.
--no-mouseFor a session: leave the mouse to the terminal.

The same settings can live in config.json, beside the profiles:

{ "mode": "auto", "classifier": { "model": "anthropic/claude-haiku-4-5-20251001", "enabled": true, "timeoutMs": 8000 }, "actions": false, "updateCheck": true, "ui": "full", "theme": "auto", "mouse": true }

classifier.timeoutMs is how long, in milliseconds, the safety check may take before the call is put to you instead: a whole number, default 8000. Anything above 60000 is treated as 60000. "ui": "inline", "theme" and "mouse": false do what --inline, --theme and --no-mouse do.

The one-shot commands

These talk to Memtro without opening a session.

CommandWhat it does
memtro ask "<question>"Ask; the answer streams to stdout. --agent for the multi-step investigation, --model <id> for a specific model.
memtro search "<query>"Search memory. --limit <n>, from 1 to 100.
memtro remember "<statement>"Save a fact, shared with the organisation; --personal keeps it to you.
memtro why <path>The pull requests, tickets, conversations and incidents behind a piece of code. --repo owner/name pins the repository.
memtro toolsEvery tool your key can use: Memtro's own, and one per connected system.
memtro call <tool>Call one of them.
memtro whoamiWho the key belongs to, its organisation, and when it expires.
memtro logoutRevoke the key on Memtro, then remove it from this computer.
memtro tokenPrint the key, for another program.
memtro updateUpdate to the latest release. --version x.y.z for a given one.

memtro help <command> prints the options for each. Every command takes --profile, --base-url, --no-browser, --json, --verbose and --no-color.

ask

memtro ask "who owns the billing migration, and when does it launch?"
memtro ask --agent "which customers opened a ticket about invoices this week, and what plan are they on?"

Memtro answers in one of two modes. chat is a single reply; agent plans, calls tools across your connected systems and comes back when it has an answer. memtro ask uses auto:chat unless you pass --agent (auto:agent), because a command that sometimes takes four seconds and sometimes four minutes cannot be scripted. See Agent mode.

The question can come on stdin. With a question on the command line as well, stdin is attached beneath it:

git diff | memtro ask "anything here that contradicts what we decided about retries?"
memtro ask < question.md

Run inside a git checkout, the repository (owner/name, from the origin remote) is sent along, so memory about that repository is preferred over the others your organisation has connected. Progress and the model's reasoning go to stderr, and only when stderr is a terminal; stdout carries the answer and nothing else.

search and remember

memtro search "deploy freeze" --limit 5
memtro remember "We moved the Baltic renewal to March 2027 because their procurement freeze runs to February."

remember saves the statement as written, through the same memtro_remember tool your assistants use. Write it the way you would want to read it in a year: self-contained, with names and dates. "Moved to March" is not a memory anybody can use later.

why

memtro why src/billing/retry.ts
memtro why --repo acme/api

A path inside the current checkout is sent as the repository spells it. --repo on its own asks about the whole repository. See Decisions.

tools and call

memtro tools
memtro call memtro_search --arg query="billing migration" --arg limit:=5
memtro call memtro_search --input '{"query": "billing migration", "limit": 5}'
memtro call hubspot --arg operation=describe --arg input:='{"operation": "search"}'
memtro call hubspot --arg operation=search --arg input:='{"object": "companies", "query": "Baltic"}'

--arg key=value sends a string; --arg key:=<json> sends a number, boolean, list or object. --input takes the whole argument object as JSON. A connected system is one tool, named after the system: operation says which of its operations to run, input carries that operation's own parameters, and the describe operation tells you what those are. A tool that changes something runs under the same rules as everywhere else in Memtro: only once an administrator has granted it by name. The tools themselves are listed in Tools and Connectors.

token

curl -H "Authorization: Bearer $(memtro token)" https://platform.memtro.com/v1/models

token hands the stored key to another program. It refuses to print the key into a terminal unless you pass --show, so it does not end up in your scrollback by accident.

Output for scripts

Every command that returns data takes --json and prints exactly one JSON document on stdout. Progress, warnings and errors go to stderr. Colour appears only on a terminal, and never with NO_COLOR set or --no-color. --verbose says which profile, host and credential store a command used and what it requested, on stderr. It never prints a key or a code, and nor does any error message.

Sessions and resuming

Each session is saved, per directory, so it can be carried on:

memtro --continue          # this directory's most recent session
memtro --resume            # pick from this directory's sessions
memtro --resume <id>       # that one

In a session, /resume does the same and /new starts afresh. If memtro is stopped while a tool call is running, the saved session can still be loaded: that call is given a result saying its outcome is not known.

More than one account

Two clients, a sandbox beside production, a self-hosted Memtro next to the hosted one: each is a profile, with its own host and its own key.

memtro --profile client --base-url https://memtro.client.internal whoami   # signs that profile in
memtro --profile client search "escalation policy"
MEMTRO_PROFILE=client memtro whoami

Without --profile or MEMTRO_PROFILE you are using default. Profile names are 1 to 32 lowercase letters, digits, - and _.

A stored key goes back only to the host that issued it. Pointing a profile at another host with --base-url or MEMTRO_BASE_URL is refused rather than sending the key somewhere new; sign that profile in again, or use another profile.

For a self-hosted Memtro, memtro --base-url https://memtro.example.com signs in there the first time and remembers the host for the profile, so every later command goes there.

CI

Do not sign in on a build machine. Create an organisation key on Dashboard → Organisation → API keys, with only the reach the job needs, and give it to the job as MEMTRO_API_KEY:

export MEMTRO_API_KEY=sk-memtro-...
memtro search "deploy freeze" --json
memtro run --allow "npm test" "make the failing test in src/cart pass"

MEMTRO_API_KEY takes precedence over any stored key and is never written anywhere. A CI job never tries to sign in: with no key it exits 3, and a refused MEMTRO_API_KEY is exit 3 saying so.

Updating and uninstalling

In a session, type /update. Outside one, run memtro update, or memtro update --version 0.2.1 for a given release. Either way, memtro downloads the release for your system, checks its SHA-256 against the release's checksums.txt, checks its build attestation, and proves the new file starts, then swaps it in. The running copy carries on; the new version starts the next time you run memtro. If anything fails, the installed file is left exactly as it was.

The build attestation. Every release tarball is attested by the release workflow, which records in Sigstore's public transparency log that this exact file was built by the release workflow in memtro-org/memtro-cli at the release's tag. Before the new file is unpacked or run, memtro update asks GitHub's API for the attestations of the tarball's SHA-256 and verifies them itself, against Sigstore's trusted root built into memtro. There are three outcomes:

  • Verified. The update goes ahead: "Updated to 0.3.0 (build attestation verified)."
  • Refused. GitHub answered and has no attestation for this file, or the attestation fails a check. Nothing is replaced, and memtro update exits 1: a file the release workflow did not build is not a memtro release.
  • Could not be checked. GitHub's API could not be reached, timed out, refused for its rate limit (60 unauthenticated requests an hour from one address) or failed, or the attestation is in a form this version cannot judge. The update goes ahead on the checksum alone and says so: "Updated to 0.3.0. The build attestation could not be checked: GitHub's API was unreachable. The download matched its published checksum." This is never used for an attestation that can be checked and is wrong.

The request to GitHub's API carries no key, no cookie and nothing from your profile.

memtro update replaces only the standalone executable (the one the install script installs, or one you built with npm run build:sea), on macOS or Linux, in a directory you can write to and other users cannot; never inside node_modules, Homebrew, Nix, a snap or a system directory. Any other copy is told why and how instead, and nothing is downloaded: a copy built from source updates with git pull and npm ci && npm run build; one in a directory you cannot write to, or one others can write to, by running the install script again.

The update check. When an interactive session starts, memtro asks GitHub for the latest release, at most once a day, in the background. It sends no key, no cookie and nothing from your Memtro profile, and it never delays start-up. When a newer release exists, a later session's start screen (inline, its first lines) says so and suggests /update: "memtro 0.3.0 is available (you have 0.2.0). Type /update." To turn the check off, set MEMTRO_NO_UPDATE_CHECK=1 or put "updateCheck": false in config.json. It is also off when CI is set, and never shown in memtro run, with --json, or when stdout is not a terminal. /update and memtro update work either way.

To uninstall, run memtro logout first if you want the key revoked on Memtro and removed from the keychain, then:

rm -rf ~/.memtro                                         # the executable (or your MEMTRO_INSTALL_DIR)
rm -rf "${XDG_CONFIG_HOME:-$HOME/.config}/memtro"        # settings and, when not in the system keychain, the key
rm -rf "${XDG_STATE_HOME:-$HOME/.local/state}/memtro"    # saved sessions and the update check's note

If you used MEMTRO_MODIFY_PATH=1, remove the # memtro lines from your shell's startup file.

Where things are kept

WhatWhere
The key, on macOSThe login Keychain, as a generic password under the service memtro-cli, one item per profile.
The key, on Linux with secret-toolThe Secret Service keyring (GNOME Keyring, KWallet).
The key, anywhere else or with MEMTRO_CREDENTIALS=filecredentials.json beside config.json, mode 0600 in a 0700 directory.
Settings: each profile's host, who it signed in as, when its key expires, and the session settings aboveconfig.json in ${XDG_CONFIG_HOME:-~/.config}/memtro. MEMTRO_CONFIG_DIR moves it and credentials.json.
Saved sessionsJSON lines under ${XDG_STATE_HOME:-~/.local/state}/memtro/sessions/, 0600 files in a 0700 directory.
What you typed at the promptPer directory in …/memtro/history/, also 0600. A line that starts with a space, a multi-line message and a line holding a Memtro key are not kept.

Saved sessions contain the contents of files the agent read and the output of commands it ran. Treat that directory as you would the repositories you use it in.

The key never appears in a process's argument list, where any user on the machine could read it with ps. The file store refuses a credentials file that other users can read, as ssh refuses a loose private key: such a key may already have been copied.

The CLI talks to the Memtro host of the profile you are using and, to check for and download updates, to GitHub's release pages for its repository, GitHub's API (api.github.com) for its build attestations, and the storage host GitHub's API names for an attestation bundle; nothing else, and it sends no telemetry. A base URL must be https://; plain http:// is accepted only for localhost, 127.0.0.1 and [::1], because a sign-in code and key would otherwise travel unencrypted. A profile that already stores a plain-http address elsewhere is refused when used, not rewritten.

Settings

VariableWhat it is for
MEMTRO_API_KEYUse this key instead of the stored one. Never stored; for CI.
MEMTRO_PROFILEWhich profile, when --profile is not given.
MEMTRO_BASE_URLWhich Memtro, when --base-url is not given. Defaults to the profile's host, else https://platform.memtro.com.
MEMTRO_CREDENTIALSfile keeps keys in the 0600 file instead of the system keychain.
MEMTRO_CONFIG_DIRSomewhere else for config.json and credentials.json.
MEMTRO_NO_UPDATE_CHECK1 turns off the daily update check.
NO_COLORNo colour.

Troubleshooting

Every command's exit code says what happened, and whether trying again could help:

Exit codeMeaningWorth retrying?
0Done. "Nothing found" is also 0, said on stderr.
1Memtro refused, or something failed; for run, the agent failed or stopped at a limit. The message says what.No
2The command line was wrong. Nothing was sent.No
3Not signed in, or the key was refused (revoked or expired).No: run memtro in a terminal, or set MEMTRO_API_KEY
4Memtro could not be reached, timed out, or is unavailable.Yes
130Interrupted.
  • memtro login says "There is no login command": there is none. Run memtro in a terminal and it signs you in.
  • Exit code 3, "Not signed in" or the key was refused: the key has been revoked or has passed its 90 days, or this profile never signed in. Run memtro in a terminal (with the same --profile, if you use one). In CI, check MEMTRO_API_KEY, which wins over anything stored.
  • Exit code 4: Memtro could not be reached or did not answer in time. This is the one failure worth retrying.
  • "Unknown command": a word after memtro that is not a command is never sent to the agent as a task. Use memtro run "<task>".
  • The browser did not open, or opened somewhere you cannot see: copy the address the terminal printed, or run again with --no-browser.
  • You are asked about everything in auto mode: the safety check is off, timing out or failing, and it fails closed to asking. /mode says when its model is off. If you set classifier.model in config.json, check that it is a model your account offers.
  • Nothing runs without asking, and the first lines say the repository is your home directory: start memtro inside a project instead.
  • A command refuses to use the stored key with another host: the profile signed in against a different Memtro. Sign it in again with the new --base-url, or use a separate profile.
  • The credentials file is refused: other users can read it. chmod 600 the file, then memtro logout and memtro to replace the key.
  • memtro token says "Not printing the key to a terminal" and exits 2: it does that on purpose. Pipe it into the program that needs it, or pass --show.
  • memtro update says this copy cannot update itself: it was not installed by the install script, or sits somewhere it cannot safely replace. Update it the way you installed it, or run the install script again.
  • memtro update refuses the download and exits 1: GitHub has no build attestation for that file, or the attestation failed a check. Nothing was replaced.
  • Click-and-drag does not select text in a session: memtro has the mouse. Hold Shift while you drag (Option in iTerm2), or start with --no-mouse or --inline; /copy copies the last answer whole.
  • A tool is missing from memtro tools or /tools: connect the system on Dashboard → Connections first. The CLI sees exactly the connections and grants your key has, as every other client does. In a session, tools that change things appear only with actions on.

Where agents remember your company

Sign in, add a provider key, create an API key and point your tools at Memtro. It takes about five minutes.

7-day free trial · card on file, nothing charged until it ends · cancel any time