Memo Agent
memo-agent is a terminal-based AI assistant application (Hermes Agent simplified version), built with TypeScript + React + Ink. It connects directly to OpenAI-compatible APIs and features persistent memory, MCP tool extensions, and intelligent context compression.
Ask AI about Memo Agent
Powered by Claude Β· Grounded in docs
I know everything about Memo Agent. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
memo-agent
memo-agent is a terminal-based AI assistant application (Hermes Agent simplified version), built with TypeScript + React + Ink. It connects directly to OpenAI-compatible APIs and features persistent memory, MCP tool extensions, and intelligent context compression.
Features
- Persistent Memory β
NOTES.mdretains context across sessions and is automatically injected into the system prompt every round; whenauto_updateis enabled, it automatically evaluates and writes at the end of each round - Session Chain Archiving β When context compression is triggered, a new session is created and linked to the old session via
parent_session_id, ensuring history is never lost - Three-Zone Context Compression β Automatically archives the middle history for extra-long conversations, preserving the first round and the most recent ~20k tokens, so conversations are never truncated
- Slash Commands β
/notes,/history,/search,/compact,/cost,/plan, and more; use/helpto see all available commands - Plan-Execute-Reflect Loop β
/plan <goal>triggers a three-phase agentic loop: planning (task breakdown), execution (per-task tool loop in dependency order), and reflection (read-only review); progress is streamed live to the UI - Task Persistence β Tasks created by the agent are stored in SQLite, scoped to the session, and survive
/resume - Recipes System β Custom
.mdtemplate files; invoke with/recipe-name [args]in one click; supportswatchPathsto auto-recommend related recipes when matching files are modified - MCP Tool Extensions β Connect to external tool servers via the Model Context Protocol
- Web Search β Built-in
WebSearchtool powered by Brave Search; configuresearch.apiKeyto let the agent search the web autonomously - Tool Result Cache β Read-only tool results are cached for 30 seconds; cleared automatically when files are modified
- Session Persistence β SQLite stores all history;
/resumerestores any historical session; supports full-text search - Profile Isolation β Multiple profiles with independent configurations, memory, and session data, completely isolated from each other
- Permission Guard β
ask/automodes; dangerous commands (e.g.,rm -rf) force confirmation; path safety restrictions; supportsdisabledToolsto completely block specified tools - RunCommand Sandbox β When
permissions.sandbox.enabled: true, child processes only inherit an explicit env-var allowlist, keeping API keys and secrets out of subprocess scope - Rich Text UI β Rendered with React + Ink, streaming output, status bar displays token usage and cost in real time
- Enhanced Input β Cursor positioning (β/β to move, edit mid-line), history navigation (β/β), queued input during streaming without loss
Installation
Via npm (recommended)
npm install -g memo-agent
Then configure your API key (choose one method):
Option A β Environment variable:
export MODEL_API_KEY=sk-...
export MODEL_BASE_URL=https://api.openai.com/v1 # optional, defaults to OpenAI
export MODEL_NAME=gpt-4o # optional, defaults to gpt-4o
memo
Option B β Config file:
mkdir -p ~/.memo-agent
cat > ~/.memo-agent/config.yaml << 'EOF'
model:
base_url: "https://api.openai.com/v1"
api_key: "sk-..."
name: gpt-4o
EOF
memo
Via npx (no install)
MODEL_API_KEY=sk-... npx memo-agent
Quick Start (from source)
Install Dependencies
npm install
Configuration
Copy the example configuration file and fill in your API details:
cp .env.example .env
MODEL_BASE_URL=https://api.openai.com/v1
MODEL_API_KEY=sk-...
MODEL_NAME=gpt-4o
Or create ~/.memo-agent/config.yaml (see Configuration File).
Launch
# Development mode (tsx hot reload)
npm run dev
# Build and run
npm run build
npm start
# Global installation from source
npm install -g .
memo
CLI Arguments
memo [options]
OPTIONS
--profile <name> Use the specified profile (default: "default")
--model <name> Override the model name in config
--resume <session-id> Resume a specific historical session
--auto Start in auto permission mode (no confirmation)
--version, -v Print version number
--help, -h Print help
Examples:
memo --profile work
memo --model gpt-4o-mini
memo --resume abc12345
memo --auto
Terminal Input Operations
| Operation | Effect |
|---|---|
β / β | Move cursor within the input line; supports mid-line insertion or deletion |
β / β | Switch through input history (up to 50 entries); β returns to current editing content |
Backspace / Delete | Delete character to the left of the cursor |
| Paste / Multi-character input | Cursor correctly jumps to the end of all characters |
| Typing during streaming | Characters are queued (gray + (queued)); can continue editing after idle |
Ctrl+C (during streaming) | Interrupt the request; already streamed content remains on screen (marked [interrupted]) |
Ctrl+C (idle, press twice) | Exit |
Status Bar
The bottom status bar displays in real time:
β memo-agent β gpt-4o tokens: 1234/128k (15%) β $0.0042 β mode:ask β profile:default
| Field | Description |
|---|---|
β / β | Streaming / idle |
tokens | Session used tokens / model limit; turns yellow above 70%, red above 85% |
$X.XXXX | Estimated session cost (USD) |
mode | Current permission mode (ask / auto) |
profile | Current profile name |
Slash Commands
Enter the following commands during a conversation:
| Command | Description |
|---|---|
/help | Display all available commands and recipes |
/plan <goal> | Run Plan-Execute-Reflect agentic loop for a goal |
/notes [show|clear] | View or clear persistent notes (NOTES.md) |
/history [n] | Show the last n sessions (default 10) |
/search <keyword> | Full-text search all historical messages |
/compact [focus description] | Manually trigger context archival compression |
/model [name] | View or switch the current model |
/cost | Show current session token consumption and estimated cost |
/clear | Clear the current session context (memory is preserved) |
/resume [session-id] | Prompt to resume a session using the --resume argument |
/profile [name] | View or switch profile |
/recipes | List installed recipes |
/mode [ask|auto] | Switch tool execution permission mode |
/exit | Exit memo-agent (alias: /quit) |
Agentic Plan Loop (/plan)
/plan <goal> triggers a three-phase autonomous loop:
- Plan β The agent calls
CreateTaskto break the goal into 3β7 tasks with explicit dependencies. No other actions occur in this phase. - Execute β Tasks are executed in topological order (blocked tasks wait for their prerequisites). Each task runs a full tool-call loop with the complete tool set.
- Reflect β After all tasks finish, the agent reviews results with read-only tools and writes a summary.
Progress events are streamed to the UI in real time:
β Planning tasks...
β Task 1/3: Set up project structure
β Set up project structure
β Task 2/3: Implement core logic
β Implement core logic
β Reflecting on results...
Recipes System
A recipe is a reusable prompt template stored as a .md file.
Storage Locations
- Global:
~/.memo-agent/recipes/ - Project-level (priority):
.memo-agent/recipes/
Recipe File Format
---
name: review
description: Perform a code review on current changes
allowedTools: [ReadFile, SearchCode, ListFiles]
---
Please perform a code review on the following changes, focusing on security, performance, and maintainability.
$ARGUMENTS
| Field | Description |
|---|---|
name | Invocation name (lowercase letters + hyphens) |
description | Description shown in the /recipes list |
allowedTools | Tools pre-authorized during recipe execution (skips permission confirmation) |
watchPaths | Auto-recommend this recipe when file paths match (optional) |
$ARGUMENTS | Placeholder for arguments passed during invocation |
Invoking a Recipe
/review src/main.ts
/fix-types
/summarize-pr
Persistent Memory
NOTES.md β Working Notes (Read/Write)
Path: ~/.memo-agent/memory/NOTES.md (or under the profile directory)
- The agent can append notes via the
WriteNotestool - When
memory.auto_update: trueis enabled, it automatically evaluates whether information is worth retaining and writes it at the end of each round; saved content is displayed in the terminal upon writing - Automatically injected into the system prompt at the start of each session
/notes showto view,/notes clearto clear
PROFILE.md β User Preferences (Read-Only)
Path: ~/.memo-agent/memory/PROFILE.md
Only editable by the user; the agent will not modify it. Suitable for placing:
I am a backend engineer, primarily using Go and TypeScript.
Code style: functional-first, avoid over-abstraction.
Please respond in Chinese; code comments in English.
Context Compression
Conversation context is managed in three zones:
ββββββββββββββββββββββββββββββββββββ
β HEAD (Anchor Zone) β system prompt + first round, never compressed
ββββββββββββββββββββββββββββββββββββ€
β MIDDLE (Archive Zone) β Replaced with LLM-generated summary when threshold exceeded
ββββββββββββββββββββββββββββββββββββ€
β TAIL (Active Zone) β Most recent ~20k tokens, fully preserved
ββββββββββββββββββββββββββββββββββββ
Trigger thresholds (adjustable in config):
- 70% context usage β Status bar warning (yellow)
- 85% context usage β Auto-trigger archival
- Manual trigger:
/compact [focus description]
Tool System
Built-in Tools
| Tool | Description | Permission |
|---|---|---|
ReadFile | Read file content, supports line range (limited to cwd / profile directory) | Read-only |
WriteFile | Create or overwrite files (limited to cwd / profile directory) | Write |
EditFile | Precise string replacement; supports replace_all: true for global replacement | Write |
ListFiles | List files using glob patterns | Read-only |
SearchCode | Regex search file content (prefers rg, falls back to grep), global result limit | Read-only |
RunCommand | Execute shell commands, 30s timeout | High-risk |
WriteNotes | Append content to NOTES.md | Write |
ReadNotes | Read current NOTES.md | Read-only |
CreateTask | Create in-session tasks (IDs start from 1, reset after /clear) | Write |
UpdateTask | Update task status, blockedBy, blocks | Write |
ListTasks | List all tasks in the current session | Read-only |
GetTask | Get task details (including dependencies) | Read-only |
SearchHistory | Full-text search historical messages (across all sessions) | Read-only |
ListSessions | List historical sessions (including session chain parent-child relationships) | Read-only |
WebSearch | Search the web via Brave Search; requires search.apiKey in config | Read-only |
MCP Tool Extensions
Configure MCP servers in config.yaml; tools are auto-registered as mcp__<server_name>__<tool_name>:
mcp_servers:
github:
type: stdio
command: npx
args: ["@modelcontextprotocol/server-github"]
filesystem:
type: stdio
command: npx
args: ["@modelcontextprotocol/server-filesystem", "/tmp"]
MCP servers connect in the background in parallel without blocking startup.
Permission System
Modes
| Mode | Behavior |
|---|---|
ask (default) | Prompt for confirmation on write operations and shell commands |
auto | Auto-execute (dangerous commands still require confirmation) |
Switch modes: /mode auto or start with --auto.
Permission Confirmation Actions
When a permission dialog appears:
Enter/yβ Allow this time (default)aβ Always allow for this sessionnβ Deny
Safe Project Directory Auto-Approval
When cwd (current working directory) is not a core/sensitive directory, WriteFile and EditFile operations within the directory are auto-approved without confirmation.
Core Directories (still require confirmation):
- Home directory itself
~(but~/projects/foois safe) - Filesystem root
/and system trees (/etc,/usr,/bin, etc.) - Sensitive subdirectories in home (
~/.ssh,~/.aws,~/.config,~/.kube, etc.)
RunCommand is not affected by this rule and always follows the original ask/auto logic.
Dangerous Command Force Confirmation
Regardless of mode, the following commands always trigger confirmation:
rm -rf, git push --force, git reset --hard, sudo, dd if=, mkfs, shutdown, kill -9, etc.
Config Allow/Deny Rules
permissions:
mode: ask
allow:
- ReadFile
- ListFiles
- SearchCode
- "RunCommand(git status)"
deny:
- "RunCommand(rm *)"
disabled_tools:
- RunCommand # Completely hidden from the model
Configuration File
File Locations
| Path | Purpose |
|---|---|
~/.memo-agent/config.yaml | Global default config |
~/.memo-agent/profiles/<name>/config.yaml | Config for a specific profile |
.env | Environment variables in the project root (highest priority) |
Full Configuration Example
# Main model configuration
model:
provider: openai # openai | custom
base_url: "${MODEL_BASE_URL}"
api_key: "${MODEL_API_KEY}"
name: gpt-4o
timeout_ms: 60000
max_tokens: 8192 # Max tokens the model may generate per response
# Auxiliary model (used for context archival compression, recommend a cheaper model)
# If omitted, the main model is used for compression as well
auxiliary:
provider: openai
base_url: "${AUX_BASE_URL}"
api_key: "${AUX_API_KEY}"
name: gpt-4o-mini
timeout_ms: 60000
max_tokens: 4096
# Persistent memory
memory:
auto_update: true # Auto-evaluate and write to NOTES.md at end of each round (default on)
max_inject_tokens: 4000 # Max tokens to inject into system prompt
# Context compression thresholds
context:
warn_threshold: 0.70 # Warning at 70%
compress_threshold: 0.85 # Auto-archive at 85%
tail_tokens: 20000 # Tokens to preserve in the active zone
# Permission control
permissions:
mode: ask # ask | auto
allow:
- ReadFile
- ListFiles
- SearchCode
- ReadNotes
- ListTasks
- GetTask
- SearchHistory
- ListSessions
deny: []
disabled_tools: [] # List of completely hidden tools, e.g. [RunCommand]
sandbox:
enabled: false # When true, child processes only see allowed_env_vars
allowed_env_vars:
- PATH
- HOME
- LANG
- TERM
- USER
- SHELL
- TZ
# Web search (optional β Brave Search)
search:
provider: brave
api_key: "${BRAVE_API_KEY}"
max_results: 5 # Results per query (1β10)
# MCP servers (optional)
mcp_servers:
github:
type: stdio
command: npx
args: ["@modelcontextprotocol/server-github"]
env:
GITHUB_TOKEN: "${GITHUB_TOKEN}"
filesystem:
type: stdio
command: npx
args: ["@modelcontextprotocol/server-filesystem", "/tmp"]
Profile Isolation
Use independent configurations, memory, and sessions for different scenarios:
~/.memo-agent/ # default profile
config.yaml
memory/
NOTES.md
PROFILE.md
sessions.db
recipes/
~/.memo-agent/profiles/work/ # work profile
config.yaml # Can use a different model, API key
memory/
sessions.db
recipes/
Switch profiles:
memo --profile work
memo --profile research
Session Management
# View last 10 sessions
/history
# View last 20 sessions
/history 20
# Full-text search historical messages (/search command used by humans)
/search "sqlite WAL mode"
# Resume a historical session (first use /history to get the session ID)
memo --resume abc12345
# Clear the current session (does not affect NOTES.md)
/clear
The model can also proactively query history via tools:
SearchHistoryβ Full-text search all historical messages, great for "what did we discuss before"ListSessionsβ List historical sessions, including session chain parent-child relationships (formed automatically after compression/archival)
Directory Structure
memo-agent/
βββ src/
β βββ cli/
β β βββ index.ts # Entry: argument parsing, startup flow
β βββ engine/
β β βββ conversationEngine.ts # Core: multi-round session loop, tool invocation
β β βββ commandRouter.ts # Slash command routing (pure functions)
β βββ model/
β β βββ client.ts # OpenAI client factory
β β βββ streaming.ts # Streaming response async generator
β βββ context/
β β βββ compressor.ts # Three-zone archival compression
β β βββ tokenBudget.ts # Token budget tracking and estimation
β β βββ promptBuilder.ts # Dynamic system prompt construction
β βββ memory/
β β βββ notesManager.ts # NOTES.md read/write
β β βββ profileReader.ts # PROFILE.md read-only
β βββ tools/
β β βββ registry.ts # Tool registry (supports disableTools)
β β βββ pathUtils.ts # Shared path security validation
β β βββ searchHistory.ts # Historical message full-text search tool
β β βββ listSessions.ts # Historical session list tool
β β βββ *.ts # Individual tool implementations (self-registering)
β βββ recipes/
β β βββ recipeRegistry.ts # Recipe loading and template expansion
β βββ session/
β β βββ db.ts # SQLite session storage (WAL + FTS5)
β βββ permissions/
β β βββ guard.ts # Centralized permission decisions
β βββ mcp/
β β βββ mcpBridge.ts # MCP protocol tool bridge
β βββ config/
β β βββ loader.ts # Configuration loading and merging
β βββ ui/
β β βββ App.tsx # Main UI (state machine)
β β βββ MessageList.tsx # Message list rendering
β β βββ StatusBar.tsx # Bottom status bar
β βββ types/
β βββ messages.ts # ChatMessage, StreamEvent
β βββ config.ts # MemoAgentConfig
β βββ errors.ts # MemoAgentError discriminated union
β βββ tool.ts # Tool interface
β βββ session.ts # SessionRow, MessageRow
βββ .memo-agent/
β βββ recipes/ # Project-level recipe files
βββ .env.example
βββ package.json
βββ tsconfig.json
βββ prd.md
Tech Stack
| Layer | Technology |
|---|---|
| Language | TypeScript 5 (strict + ESM) |
| Terminal UI | React 18 + Ink 5 |
| Database | better-sqlite3 (WAL mode + FTS5 full-text index) |
| Model SDK | openai (OpenAI-compatible API) |
| Tool Protocol | @modelcontextprotocol/sdk |
| Config Parsing | js-yaml + dotenv |
| Build | tsx (dev) / tsc (production) |
Development
# Type check
npm run typecheck
# Development mode (tsx, no build needed)
npm run dev
# Production build
npm run build
# Run the built artifact
npm start
Adding Custom Tools
- Create
myTool.tsundersrc/tools/ - Implement the
Toolinterface and callregisterTool(myTool)at the end of the file - Add
import "./myTool.js"insrc/tools/index.ts
import type { Tool, ToolContext, ToolResult } from "../types/tool.js";
import { registerTool } from "./registry.js";
const myTool: Tool = {
name: "MyTool",
description: "Describe the tool's purpose",
inputSchema: {
type: "object",
properties: {
param: { type: "string", description: "Parameter description" },
},
required: ["param"],
},
maxResultChars: 10_000,
isReadOnly(): boolean { return true; },
isEnabled(): boolean { return true; },
async call(input: Record<string, unknown>, _ctx: ToolContext): Promise<ToolResult> {
const param = input["param"] as string;
return { content: `Result: ${param}` };
},
};
registerTool(myTool);
Security Notes
- Path Restriction:
ReadFile,WriteFile,EditFileonly allow operating on files within the current working directory or profile directory; out-of-bounds access returns an error - Safe Project Directory: When working in non-core directories (project directories), file write operations are auto-approved; the home directory itself, system paths, and sensitive hidden directories still require confirmation (see Safe Project Directory Auto-Approval)
- Injection Scanning:
NOTES.md,PROFILE.md, and recipe files are automatically scanned for prompt injection signatures before injection; if detected, injection is skipped and a warning is displayed in the UI - Command Interception:
RunCommanddangerous command blacklist forces confirmation in any mode - FTS5 Security: Search queries are automatically escaped to prevent FTS5 syntax injection
- Tool Masking: Specified tools can be completely removed from the model's view via
permissions.disabled_tools - Log Sanitization: Runtime warnings are output via UI event stream or stderr without interfering with terminal UI rendering
- RunCommand Sandbox: When
permissions.sandbox.enabled: true, spawned processes inherit only the explicitly listed env vars β API keys and other secrets are stripped from the subprocess environment
