feat(cli): remove mem0 version subcommand from md files and fix help --json in the Python CLI (#6773)

This commit is contained in:
Kartik
2026-08-03 08:59:47 -07:00
committed by GitHub
parent ea6fd3b457
commit 21aae599be
12 changed files with 113 additions and 39 deletions
+1 -2
View File
@@ -60,9 +60,8 @@ mem0 delete <memory-id>
| `mem0 entity` | List or delete entities (users, agents, apps, runs) | | `mem0 entity` | List or delete entities (users, agents, apps, runs) |
| `mem0 event` | Inspect background processing events (bulk deletes, large add jobs) | | `mem0 event` | Inspect background processing events (bulk deletes, large add jobs) |
| `mem0 status` | Verify API connection and display current project | | `mem0 status` | Verify API connection and display current project |
| `mem0 version` | Print the CLI version |
Run `mem0 <command> --help` for detailed usage on any command. Run `mem0 <command> --help` for detailed usage on any command, or `mem0 --version` to print the CLI version.
## Agent mode ## Agent mode
+2 -8
View File
@@ -232,14 +232,6 @@ Verify your API connection and display the current project.
mem0 status mem0 status
``` ```
### `mem0 version`
Print the CLI version.
```bash
mem0 version
```
## Agent mode ## Agent mode
Pass `--agent` (or its alias `--json`) as a **global flag** on any command to get output designed for AI agent tool loops: Pass `--agent` (or its alias `--json`) as a **global flag** on any command to get output designed for AI agent tool loops:
@@ -298,6 +290,8 @@ These flags are available on all commands:
| `--base-url` | Override the configured API base URL for this request | | `--base-url` | Override the configured API base URL for this request |
| `-o, --output` | Set the output format | | `-o, --output` | Set the output format |
`mem0 --version` prints the CLI version. It is only valid before a subcommand, not after one.
## Environment variables ## Environment variables
| Variable | Description | | Variable | Description |
+1 -1
View File
@@ -51,7 +51,7 @@ pnpm link --global
# Now use it like a normal CLI # Now use it like a normal CLI
mem0 --help mem0 --help
mem0 version mem0 --version
``` ```
> **Warning:** If you also have the Python CLI installed, both register the `mem0` command. The last one linked/installed wins. Unlink with `pnpm unlink --global`. > **Warning:** If you also have the Python CLI installed, both register the `mem0` command. The last one linked/installed wins. Unlink with `pnpm unlink --global`.
+1 -6
View File
@@ -1,5 +1,5 @@
/** /**
* Utility commands: status, version, import. * Utility commands: status, import.
*/ */
import fs from "node:fs"; import fs from "node:fs";
@@ -8,7 +8,6 @@ import type { Backend } from "../backend/base.js";
import { colors, printError, printSuccess, timedStatus } from "../branding.js"; import { colors, printError, printSuccess, timedStatus } from "../branding.js";
import { formatAgentEnvelope, formatJsonEnvelope } from "../output.js"; import { formatAgentEnvelope, formatJsonEnvelope } from "../output.js";
import { setCurrentCommand } from "../state.js"; import { setCurrentCommand } from "../state.js";
import { CLI_VERSION } from "../version.js";
const { brand, dim, success, error: errorColor } = colors; const { brand, dim, success, error: errorColor } = colors;
@@ -82,10 +81,6 @@ export async function cmdStatus(
console.log(); console.log();
} }
export function cmdVersion(): void {
console.log(` ${brand("◆ Mem0")} CLI v${CLI_VERSION}`);
}
export async function cmdImport( export async function cmdImport(
backend: Backend, backend: Backend,
filePath: string, filePath: string,
+2 -2
View File
@@ -858,8 +858,8 @@ program
.addHelpText("after", "\nExamples:\n $ mem0 help\n $ mem0 help --json") .addHelpText("after", "\nExamples:\n $ mem0 help\n $ mem0 help --json")
.action((opts) => { .action((opts) => {
// opts.json is set when `mem0 help --json` is used (subcommand flag). // opts.json is set when `mem0 help --json` is used (subcommand flag).
// program.opts().json is set when the root --json global flag was used first. // program.opts().json/.agent is set when a root global flag was used first.
if (opts.json || program.opts().json) { if (opts.json || program.opts().json || program.opts().agent) {
// Load spec from parent directory // Load spec from parent directory
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const __dirname = path.dirname(fileURLToPath(import.meta.url));
const specPath = path.join(__dirname, "..", "..", "cli-spec.json"); const specPath = path.join(__dirname, "..", "..", "cli-spec.json");
+17 -7
View File
@@ -44,15 +44,25 @@ describe("CLI Integration — help and version", () => {
expect(result.stdout).toContain("search"); expect(result.stdout).toContain("search");
}); });
it("help --json produces valid JSON", () => { it("prints the version with --version, and has no version subcommand", () => {
const result = run(["help", "--json"]); const flag = run(["--version"]);
expect(result.exitCode).toBe(0); expect(flag.exitCode).toBe(0);
const parsed = JSON.parse(result.stdout); expect(flag.stdout).toContain("Mem0");
// spec may have cli.name or top-level name expect(run(["version"]).exitCode).not.toBe(0);
const name = parsed.name ?? parsed.cli?.name;
expect(name).toBe("mem0");
}); });
it.each([["help", "--json"], ["--json", "help"], ["--agent", "help"]])(
"%s %s produces valid JSON",
(...args) => {
const result = run(args);
expect(result.exitCode).toBe(0);
const parsed = JSON.parse(result.stdout);
// spec may have cli.name or top-level name
const name = parsed.name ?? parsed.cli?.name;
expect(name).toBe("mem0");
},
);
it("shows add help", () => { it("shows add help", () => {
const result = run(["add", "--help"]); const result = run(["add", "--help"]);
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
+31 -1
View File
@@ -16,18 +16,45 @@ const OPENAPI_PATH = path.join(
"openapi.json", "openapi.json",
); );
const KNOWN_UNSURFACED: Record<string, Record<string, string>> = {}; const KNOWN_UNSURFACED: Record<string, Record<string, string>> = {
"/v3/memories/add/": {
includes: "extraction hint, no CLI flag yet",
excludes: "extraction hint, no CLI flag yet",
enable_graph: "graph memory toggle, no CLI flag yet",
output_format: "response envelope is pinned by the CLI",
prompt_profile_id: "no CLI flag yet",
temporal_reasoning: "no CLI flag yet",
timezone: "no CLI flag yet",
observation_datetime: "no CLI flag yet, --timestamp backdates instead",
observation_date: "no CLI flag yet, --timestamp backdates instead",
},
"/v3/memories/search/": {
categories: "expressible through --filter",
metadata: "expressible through --filter",
},
"/v3/memories/": {
start_date: "covered by --after via filters.created_at.gte",
end_date: "covered by --before via filters.created_at.lte",
categories: "covered by --category via filters.categories",
fields: "no CLI flag yet",
keywords: "no CLI flag yet",
},
};
const ADD_MAPPING: Record<string, string[]> = { const ADD_MAPPING: Record<string, string[]> = {
messages: ["--messages", "--file", "text"], messages: ["--messages", "--file", "text"],
user_id: ["--user-id"], user_id: ["--user-id"],
agent_id: ["--agent-id"], agent_id: ["--agent-id"],
app_id: ["--app-id"],
run_id: ["--run-id"], run_id: ["--run-id"],
metadata: ["--metadata"], metadata: ["--metadata"],
expiration_date: ["--expires"], expiration_date: ["--expires"],
custom_instructions: ["--custom-instructions"], custom_instructions: ["--custom-instructions"],
custom_categories: ["--custom-categories"], custom_categories: ["--custom-categories"],
infer: ["--no-infer"], infer: ["--no-infer"],
immutable: ["--immutable"],
structured_data_schema: ["--structured-data-schema"],
timestamp: ["--timestamp"],
}; };
const SEARCH_MAPPING: Record<string, string[]> = { const SEARCH_MAPPING: Record<string, string[]> = {
@@ -38,6 +65,7 @@ const SEARCH_MAPPING: Record<string, string[]> = {
threshold: ["--threshold"], threshold: ["--threshold"],
rerank: ["--rerank"], rerank: ["--rerank"],
reference_date: ["--reference-date"], reference_date: ["--reference-date"],
fields: ["--fields"],
}; };
const LIST_MAPPING: Record<string, string[]> = { const LIST_MAPPING: Record<string, string[]> = {
@@ -50,6 +78,8 @@ const LIST_MAPPING: Record<string, string[]> = {
"--before", "--before",
], ],
show_expired: ["--show-expired"], show_expired: ["--show-expired"],
page: ["--page"],
page_size: ["--page-size"],
}; };
function documentedFields(endpoint: string): string[] { function documentedFields(endpoint: string): string[] {
+2 -8
View File
@@ -241,14 +241,6 @@ Verify your API connection and display the current project.
mem0 status mem0 status
``` ```
### `mem0 version`
Print the CLI version.
```bash
mem0 version
```
## Agent mode ## Agent mode
Pass `--agent` (or its alias `--json`) as a **global flag** on any command to get output designed for AI agent tool loops: Pass `--agent` (or its alias `--json`) as a **global flag** on any command to get output designed for AI agent tool loops:
@@ -307,6 +299,8 @@ These flags are available on all commands:
| `--base-url` | Override the configured API base URL for this request | | `--base-url` | Override the configured API base URL for this request |
| `-o, --output` | Set the output format | | `-o, --output` | Set the output format |
`mem0 --version` prints the CLI version. It is only valid before a subcommand, not after one.
## Environment variables ## Environment variables
| Variable | Description | | Variable | Description |
+1 -1
View File
@@ -80,7 +80,7 @@ mem0 --help
# Using Python directly (with venv activated) # Using Python directly (with venv activated)
source .venv/bin/activate source .venv/bin/activate
mem0 --help mem0 --help
mem0 version mem0 --version
# Or run without activating # Or run without activating
.venv/bin/mem0 --help .venv/bin/mem0 --help
+4 -2
View File
@@ -1350,8 +1350,10 @@ def help(
mem0 help mem0 help
mem0 help --json mem0 help --json
""" """
if json: from mem0_cli.state import is_agent_mode
console.print(_json.dumps(_build_help_json(), indent=2))
if json or is_agent_mode():
console.print_json(_json.dumps(_build_help_json()))
else: else:
console.print( console.print(
f"[{BRAND_COLOR}]◆ mem0 CLI[/] v{__version__} — The Memory Layer for AI Agents\n" f"[{BRAND_COLOR}]◆ mem0 CLI[/] v{__version__} — The Memory Layer for AI Agents\n"
+26
View File
@@ -7,6 +7,7 @@ boundaries).
from __future__ import annotations from __future__ import annotations
import json
import os import os
import re import re
import subprocess import subprocess
@@ -84,6 +85,31 @@ class TestCLIIntegration:
assert "add" in result.stdout assert "add" in result.stdout
assert "search" in result.stdout assert "search" in result.stdout
def test_version_flag_only(self):
from mem0_cli import __version__
flag = _run(["--version"])
assert flag.returncode == 0
assert __version__ in flag.stdout
assert _run(["version"]).returncode != 0
@pytest.mark.parametrize(
"args",
[["help", "--json"], ["--json", "help"], ["help", "--agent"], ["--agent", "help"]],
)
def test_help_json_produces_valid_json(self, args):
result = _run(args)
assert result.returncode == 0
spec = json.loads(result.stdout)
assert spec["name"] == "mem0"
assert "add" in spec["commands"]
def test_help_without_json_is_text(self):
result = _run(["help"])
assert result.returncode == 0
with pytest.raises(json.JSONDecodeError):
json.loads(result.stdout)
def test_add_help(self): def test_add_help(self):
result = _run(["add", "--help"]) result = _run(["add", "--help"])
assert result.returncode == 0 assert result.returncode == 0
+25 -1
View File
@@ -10,18 +10,39 @@ from mem0_cli.app import app
REPO_ROOT = Path(__file__).resolve().parents[3] REPO_ROOT = Path(__file__).resolve().parents[3]
OPENAPI_PATH = REPO_ROOT / "docs" / "openapi.json" OPENAPI_PATH = REPO_ROOT / "docs" / "openapi.json"
KNOWN_UNSURFACED: dict[tuple[str, str], str] = {} KNOWN_UNSURFACED: dict[tuple[str, str], str] = {
("/v3/memories/add/", "includes"): "extraction hint, no CLI flag yet",
("/v3/memories/add/", "excludes"): "extraction hint, no CLI flag yet",
("/v3/memories/add/", "enable_graph"): "graph memory toggle, no CLI flag yet",
("/v3/memories/add/", "output_format"): "response envelope is pinned by the CLI",
("/v3/memories/add/", "prompt_profile_id"): "no CLI flag yet",
("/v3/memories/add/", "temporal_reasoning"): "no CLI flag yet",
("/v3/memories/add/", "timezone"): "no CLI flag yet",
("/v3/memories/add/", "observation_datetime"): "no CLI flag yet, --timestamp backdates instead",
("/v3/memories/add/", "observation_date"): "no CLI flag yet, --timestamp backdates instead",
("/v3/memories/search/", "categories"): "expressible through --filter",
("/v3/memories/search/", "metadata"): "expressible through --filter",
("/v3/memories/", "start_date"): "covered by --after via filters.created_at.gte",
("/v3/memories/", "end_date"): "covered by --before via filters.created_at.lte",
("/v3/memories/", "categories"): "covered by --category via filters.categories",
("/v3/memories/", "fields"): "no CLI flag yet",
("/v3/memories/", "keywords"): "no CLI flag yet",
}
ADD_MAPPING: dict[str, list[str]] = { ADD_MAPPING: dict[str, list[str]] = {
"messages": ["messages", "file", "text"], "messages": ["messages", "file", "text"],
"user_id": ["user_id"], "user_id": ["user_id"],
"agent_id": ["agent_id"], "agent_id": ["agent_id"],
"app_id": ["app_id"],
"run_id": ["run_id"], "run_id": ["run_id"],
"metadata": ["metadata"], "metadata": ["metadata"],
"expiration_date": ["expires"], "expiration_date": ["expires"],
"custom_instructions": ["custom_instructions"], "custom_instructions": ["custom_instructions"],
"custom_categories": ["custom_categories"], "custom_categories": ["custom_categories"],
"infer": ["no_infer"], "infer": ["no_infer"],
"immutable": ["immutable"],
"structured_data_schema": ["structured_data_schema"],
"timestamp": ["timestamp"],
} }
SEARCH_MAPPING: dict[str, list[str]] = { SEARCH_MAPPING: dict[str, list[str]] = {
@@ -32,11 +53,14 @@ SEARCH_MAPPING: dict[str, list[str]] = {
"threshold": ["threshold"], "threshold": ["threshold"],
"rerank": ["rerank"], "rerank": ["rerank"],
"reference_date": ["reference_date"], "reference_date": ["reference_date"],
"fields": ["fields"],
} }
LIST_MAPPING: dict[str, list[str]] = { LIST_MAPPING: dict[str, list[str]] = {
"filters": ["user_id", "agent_id", "run_id", "category", "after", "before"], "filters": ["user_id", "agent_id", "run_id", "category", "after", "before"],
"show_expired": ["show_expired"], "show_expired": ["show_expired"],
"page": ["page"],
"page_size": ["page_size"],
} }