Agent-first CLI for capturing and querying development decisions via a Qdrant vector store.
GitHub: https://github.com/llipe/memo-cli
@llipe.com/memo-cli lets AI agents and developers record architectural decisions, integration points, and structural choices during development — then retrieve them semantically at any time.
- Requirements
- Installation
- Getting Started
- Usage Guide
- Command Reference
- Agent Integration
- Bootstrap Workflow
- Development
- Project Structure
- License
| Tool | Version | Notes |
|---|---|---|
| Node.js | ≥ 24 LTS | Required for native ESM support |
| Qdrant | ≥ 1.7 | Local Docker or Qdrant Cloud (free tier) |
| OpenAI API key | — | For text embeddings (text-embedding-3-small) |
npm install -g @llipe.com/memo-cli
memo --versiongit clone https://github.com/llipe/memo-cli.git
cd memo-cli
pnpm install
pnpm run build
./dist/index.js --helpRun Qdrant locally with Docker:
docker run -p 6333:6333 qdrant/qdrantOr use Qdrant Cloud (free tier available).
Create a .env file in your project root (or export the vars in your shell):
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY= # leave empty for local unauthenticated Qdrant
EMBEDDINGS_PROVIDER=openai
EMBEDDINGS_API_KEY=sk-... # your OpenAI API keymemo setup initThis launches an interactive wizard that creates a memo.config.json in the current directory, establishing the repository's identity (repo name, organization, domain).
memo write \
--rationale "Chose Qdrant over Pinecone for self-hosting flexibility and payload filtering." \
--tags "architecture,storage,qdrant" \
--entry-type decision \
--source agentmemo search "why did we choose the vector database"That's it — the core loop is write decisions → search them later.
Every repository using memo needs a memo.config.json file. The setup command creates it.
memo setup initThe wizard will prompt for:
- Repository name — auto-detected from git remote if available
- Organization — your team or company identifier
- Domain — product area (e.g.,
payments,auth,frontend) - Related repos — other repositories this repo integrates with (optional)
memo setup init \
--repo my-service \
--org my-company \
--domain backend \
--relates-to "auth-service,api-gateway"memo setup show # display effective config
memo setup validate # check config validity (exit 0 = valid){
"schema_version": "1",
"repo": "my-service",
"org": "my-company",
"domain": "backend",
"relates_to": ["auth-service", "api-gateway"],
"defaults": {}
}The write command captures a decision and stores it in Qdrant with a vector embedding for semantic retrieval.
memo write \
--rationale "Adopted JWT with RS256 for service-to-service auth. Short-lived tokens (15 min) with refresh rotation." \
--tags "auth,jwt,security" \
--entry-type decision \
--source agent| Flag | Required | Default | Description |
|---|---|---|---|
--rationale |
Yes | — | Decision text (1–5000 chars) |
--tags |
Yes | — | Comma-separated tags (2–5, kebab-case) |
--entry-type |
No | decision |
decision | integration_point | structure |
--source |
No | from config | agent | scan | manual |
--commit |
No | — | Associated git commit SHA |
--story |
No | — | Associated story/task identifier |
--files-modified |
No | — | Comma-separated file paths |
--relates-to |
No | — | Comma-separated related repos |
--on-duplicate |
No | — | Duplicate action: consolidate | update | replace | create-new |
--json |
No | false |
Output as JSON |
If you write an entry with the same repo + commit + story + entry_type + source combination, memo detects the duplicate:
- Interactive (TTY): prompts you to choose an action (consolidate, update, replace, create new)
- Agent/JSON mode: requires
--on-duplicateflag to resolve programmatically
# Agent mode: automatically merge duplicates
memo write \
--rationale "Updated JWT decision to include ECDSA as fallback." \
--tags "auth,jwt,security,ecdsa" \
--entry-type decision \
--source agent \
--commit abc1234 \
--on-duplicate consolidate \
--json| Type | When to use |
|---|---|
decision |
Architectural or technical decisions made during a task |
integration_point |
How another system, service, or module is integrated |
structure |
Module-level or architectural structure (typically from bootstrap) |
Find decisions using natural language queries. Memo embeds your query and performs semantic vector search against all stored entries.
memo search "how do we handle authentication"# Only search within this repo (default)
memo search "database connection pooling" --scope repo
# Include related repos defined in config
memo search "how does the auth service validate tokens" --scope related
# Require specific tags (AND semantics)
memo search "API rate limiting" --tags "api,rate-limit"
# Filter by entry type
memo search "module boundaries" --entry-type structure
# Limit results
memo search "caching strategy" --limit 5
# Combine filters
memo search "event publishing" \
--scope related \
--tags "events,kafka" \
--entry-type integration_point \
--limit 3| Flag | Default | Description |
|---|---|---|
--scope |
repo |
repo (this repo only) or related (include relates_to repos) |
--tags |
— | Comma-separated tags to require (AND semantics) |
--entry-type |
— | Filter: decision | integration_point | structure |
--source |
— | Filter: agent | scan | manual |
--limit |
5 |
Maximum results to return |
--json |
false |
Output as JSON |
Human mode output shows:
1. (94%) Adopted JWT with RS256 for service-to-service auth...
repo: my-service tags: auth, jwt, security type: decision
source: agent confidence: high 2026-04-10T15:30:00Z
2. (87%) Auth service exposes /validate endpoint for token...
repo: auth-service tags: auth, api, validation type: integration_point
source: agent confidence: high 2026-04-09T10:00:00Z
JSON mode (--json) returns the full machine-readable payload:
{
"query": "how do we handle authentication",
"filters": { "scope": "repo", "repos": ["my-service"] },
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"repo": "my-service",
"org": "my-company",
"rationale": "Adopted JWT with RS256...",
"tags": ["auth", "jwt", "security"],
"entry_type": "decision",
"similarity": 0.94,
...
}
],
"count": 2
}Browse entries chronologically (newest first) with optional date-range filtering.
memo list # last 20 entries for this repo
memo list --limit 50 # last 50 entries# Entries from a specific date
memo list --from 2026-04-01
# Entries in a date range
memo list --from 2026-04-01 --to 2026-04-10
# Full ISO 8601 timestamps also work
memo list --from 2026-04-01T00:00:00Z --to 2026-04-10T23:59:59Zmemo list --tags "auth,jwt"
memo list --entry-type decision
memo list --source agent --limit 10| Flag | Default | Description |
|---|---|---|
--scope |
repo |
repo or related |
--tags |
— | Comma-separated tag filter |
--entry-type |
— | Filter by entry type |
--source |
— | Filter by source |
--from |
— | Start date (YYYY-MM-DD or ISO 8601) |
--to |
— | End date (YYYY-MM-DD or ISO 8601) |
--limit |
20 |
Maximum entries |
--json |
false |
Output as JSON |
memo setup show # human-readable
memo setup show --json # JSON outputmemo setup validate
# Exit code 0 = valid, 1 = errors foundmemo setup init --forceBrowse all unique tags stored in your knowledge base, with counts and flexible scope.
memo tags list# Include related repos from config
memo tags list --scope related
# Sort alphabetically (default: frequency)
memo tags list --sort alpha
# JSON output for agent consumption
memo tags list --json| Flag | Default | Description |
|---|---|---|
--scope |
repo |
repo or related |
--sort |
frequency |
frequency (count desc) or alpha |
--json |
false |
Output as JSON |
Discover what organizations, repositories, and domains have entries in your Qdrant collection — without any scope restriction.
memo inspectOutputs three grouped sections: Organizations, Repositories (with org and domain annotations), and Domains.
# Only repositories
memo inspect --repos
# Only organizations
memo inspect --orgs
# Only domains
memo inspect --domains
# JSON output
memo inspect --json| Flag | Default | Description |
|---|---|---|
--orgs |
— | Show only organizations |
--repos |
— | Show only repositories |
--domains |
— | Show only domains |
--json |
false |
Output as JSON |
When no filter flags are passed, all three facets are shown.
Safely delete individual entries or bulk-delete by repository or organization. Always confirms before deleting.
memo delete --id 550e8400-e29b-41d4-a716-446655440000Memo shows a preview of the matching entry and asks for confirmation.
# Delete all entries for a repository
memo delete --all-by-repo my-service
# Delete all entries for an organization
memo delete --all-by-org my-companyMemo shows a count of matching entries and asks for confirmation.
memo delete --id <id> --yes
memo delete --all-by-repo my-service --yesIn agent mode (--source agent or when --json is used for single deletes), bulk flags are not available via --json. Single-entry deletion supports --json output:
memo delete --id <id> --json
# outputs: { "deleted": true, "id": "...", "scope": "single", "count": 1 }| Flag | Default | Description |
|---|---|---|
--id |
— | Delete a single entry by UUID |
--all-by-repo |
— | Delete all entries for the given repo name |
--all-by-org |
— | Delete all entries for the given organization |
--yes |
false |
Skip confirmation prompt |
--json |
false |
JSON output (single-delete only) |
--id,--all-by-repo, and--all-by-orgare mutually exclusive.
| Command | Purpose | Key Flags |
|---|---|---|
memo setup init |
Create memo.config.json |
--repo, --org, --domain, --relates-to, --force |
memo setup show |
Display current config | --json |
memo setup validate |
Check config validity | — |
memo write |
Capture a decision | --rationale, --tags, --entry-type, --source, --on-duplicate, --json |
memo search <query> |
Semantic search | --scope, --tags, --entry-type, --limit, --json |
memo list |
Chronological listing | --from, --to, --tags, --limit, --json |
memo tags list |
Browse unique tags | --scope, --sort, --json |
memo inspect |
Discover orgs/repos/domains | --orgs, --repos, --domains, --json |
memo delete |
Delete entries | --id, --all-by-repo, --all-by-org, --yes, --json |
| Flag | Description |
|---|---|
--version |
Print version number |
--help |
Show help text |
| Code | Meaning |
|---|---|
0 |
Success |
1 |
User error (bad input, missing config) |
2 |
System error (Qdrant unreachable, API failure) |
Memo is designed to be used by AI coding agents as part of their workflow. All commands support --json for machine-readable output.
# 1. Before starting a task — search for prior decisions
memo search "authentication strategy for microservices" --json
# 2. After completing a task — write the decision
memo write \
--rationale "Implemented OAuth2 with PKCE for the mobile client. Chose this over implicit flow for security." \
--tags "auth,oauth2,mobile" \
--entry-type decision \
--source agent \
--commit "$(git rev-parse HEAD)" \
--story "PROJ-123" \
--on-duplicate consolidate \
--json
# 3. Verify the write
memo search "OAuth2 mobile client" --json | jq '.count'Agents need these environment variables set:
export QDRANT_URL=http://localhost:6333
export EMBEDDINGS_API_KEY=sk-...No .env file needed — agents should inject variables directly into the process environment.
This repository ships a reusable agent skill at .github/skills/memo-cli-usage/SKILL.md that teaches AI agents (and developers) how to operate memo-cli correctly and consistently.
The skill is self-contained and installable — copy the memo-cli-usage/ folder into any repository's .github/skills/ directory and reference it from that repo's agent registry.
| Section | Description |
|---|---|
| When to write / when to search | Precise trigger tables — which moment calls for a memo write, which calls for a memo search first |
repo, org, domain explained |
What each identity field means, how to choose values, and why they matter for scoping |
relates_to explained |
When to add a repo, how cross-repo queries work, and how to avoid over-populating the list |
| Tag strategy | Five-layer taxonomy (domain, technology, entry nature, story ref, scope), naming rules, and ready-made examples |
| Writing quality | The context + decision + rationale formula with side-by-side good/bad examples |
| Intent & outcome entries | How to narrate agent work as it happens — write before starting, write after finishing |
| Recording file changes | How to annotate which files changed and explain why those were the right files |
| Recording config decisions | Env vars, storage layout, feature flags, schema versions — what to capture and why |
| Multi-developer & cross-session | Shared KB, session continuity for stateless agents, multi-day work pattern |
| Memory scope decision tree | When to use IDE/agent memory (/memories/) vs. memo-cli |
| Safe operation guardrails | Non-destructive defaults, --json mode rules, error handling, credential safety |
# 1. Copy the skill
cp -r .github/skills/memo-cli-usage /path/to/your-repo/.github/skills/
# 2. Register it in your AGENTS.md or skill registry:
# | memo-cli-usage | .github/skills/memo-cli-usage/ | Agent guidance for memo-cli | Any agent |
# 3. Install memo-cli
pnpm add -D @llipe.com/memo-cli
# 4. Initialize the repo
npx memo setup init --repo <name> --org <org> --domain <domain>Once the skill is registered, any agent (GitHub Copilot, Claude, GPT-4, etc.) that loads it will know how to record decisions, restore session context, tag consistently, and stay safe.
To populate memo with decisions from an existing codebase, use the bootstrap workflow:
- Feed key artifacts (README, architecture docs, config files) to an AI agent with the bootstrap prompt template
- The agent produces JSON entries conforming to the entry schema
- Validate the JSON with the included validation script
- Write entries via
memo write
See docs/bootstrap-guide.md for the full prompt template, JSON conversion examples, and validation steps.
# Validate a bootstrap JSON file
node --loader ts-node/esm scripts/validate-bootstrap.ts ./my-bootstrap.jsongit clone https://github.com/llipe/memo-cli.git
cd memo-cli
pnpm install
cp .env.example .env # configure credentials| Script | Description |
|---|---|
pnpm run build |
Compile TypeScript to dist/ |
pnpm run build:watch |
Compile in watch mode |
pnpm run typecheck |
Type-check without emitting |
pnpm run lint |
ESLint (v9 flat config, strict type-checked) |
pnpm run lint:fix |
ESLint with auto-fix |
pnpm run format |
Prettier format |
pnpm run format:check |
Check formatting without writing |
pnpm run test |
Run Jest test suite |
pnpm run test:coverage |
Run Jest with coverage report |
pnpm run test # all tests
pnpm run test -- --testPathPattern=write # specific module
pnpm run test:coverage # with coverage report202+ test cases across unit and integration layers. Coverage threshold: 80% lines/functions/statements.
- CI runs on every push: typecheck → lint → test → build → audit
- Publish triggers on semver tag push (
v*.*.*) → npm publish
src/
├── index.ts # CLI entry point (Commander root)
├── commands/
│ ├── setup.ts # memo setup (init / show / validate)
│ ├── write.ts # memo write (with duplicate detection)
│ ├── search.ts # memo search (semantic + pre-filters)
│ ├── list.ts # memo list (chronological + date range)
│ ├── tags.ts # memo tags list (unique tags with counts)
│ ├── inspect.ts # memo inspect (org/repo/domain facets)
│ └── delete.ts # memo delete (safe single + bulk delete)
├── lib/
│ ├── qdrant.ts # Qdrant collection management & queries
│ ├── facets.ts # Scroll-based facet aggregation
│ ├── embeddings.ts # Embeddings adapter interface & factory
│ ├── config.ts # Config file I/O & validation
│ ├── registry.ts # Related-repo resolution
│ ├── output.ts # Human/JSON output formatting
│ ├── errors.ts # Typed error hierarchy
│ ├── dedupe.ts # Deduplication & merge strategies
│ ├── search-filters.ts # Search pre-filter builder
│ ├── list-filters.ts # List pre-filter builder (date range)
│ ├── retry.ts # Exponential backoff retry
│ └── debug.ts # Debug logging (MEMO_DEBUG)
├── adapters/
│ └── openai-embeddings.ts # OpenAI text-embedding-3-small
└── types/
├── entry.ts # EntryPayload Zod schema
├── config.ts # MemoConfig Zod schema
└── cli.ts # Shared CLI interfaces
tests/
├── unit/ # Unit tests (lib, adapters, commands)
└── integration/ # Integration tests (commands, qdrant)
scripts/
├── run-jest.mjs # Jest argument forwarder
└── validate-bootstrap.ts # Bootstrap JSON validator
docs/
├── product-context.md # Product strategy & roadmap
├── technical-guidelines.md # Technical standards
├── system-overview.md # Architecture overview
├── data-model.md # Data entities & schema
├── bootstrap-guide.md # Bootstrap prompt & workflow
└── requirements/
└── prd-001-mvp.md # MVP product requirements
MIT