feat(cli): add a version subcommand and document the --filter JSON shape (#6907)
This commit is contained in:
+10
-1
@@ -36,7 +36,16 @@ const COMMAND_GROUPS: { panel: string; commands: string[] }[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
panel: "Management",
|
panel: "Management",
|
||||||
commands: ["init", "status", "import", "help", "entity", "event", "config"],
|
commands: [
|
||||||
|
"init",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"import",
|
||||||
|
"help",
|
||||||
|
"entity",
|
||||||
|
"event",
|
||||||
|
"config",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+17
-3
@@ -95,6 +95,10 @@ async function getBackendOnly(
|
|||||||
return (await getBackendAndConfig(apiKey, baseUrl)).backend;
|
return (await getBackendAndConfig(apiKey, baseUrl)).backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printVersion(): void {
|
||||||
|
console.log(` ${colors.brand("◆ Mem0")} CLI v${CLI_VERSION}`);
|
||||||
|
}
|
||||||
|
|
||||||
function checkAgentMode(): boolean {
|
function checkAgentMode(): boolean {
|
||||||
const rootOpts = program.opts();
|
const rootOpts = program.opts();
|
||||||
const isAgent = !!(rootOpts.json || rootOpts.agent);
|
const isAgent = !!(rootOpts.json || rootOpts.agent);
|
||||||
@@ -154,7 +158,7 @@ program
|
|||||||
.enablePositionalOptions()
|
.enablePositionalOptions()
|
||||||
.option("--version", "Show version and exit.")
|
.option("--version", "Show version and exit.")
|
||||||
.on("option:version", () => {
|
.on("option:version", () => {
|
||||||
console.log(` ${colors.brand("◆ Mem0")} CLI v${CLI_VERSION}`);
|
printVersion();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
})
|
})
|
||||||
.option("--json", "Output as JSON for agent/programmatic use.")
|
.option("--json", "Output as JSON for agent/programmatic use.")
|
||||||
@@ -387,7 +391,10 @@ program
|
|||||||
)
|
)
|
||||||
.option("--rerank", "Enable reranking (Platform only).", false)
|
.option("--rerank", "Enable reranking (Platform only).", false)
|
||||||
.option("--keyword", "Use keyword search.", false)
|
.option("--keyword", "Use keyword search.", false)
|
||||||
.option("--filter <json>", "Advanced filter expression (JSON).")
|
.option(
|
||||||
|
"--filter <json>",
|
||||||
|
'Advanced filter as JSON: {"AND": [...]} or {"OR": [...]}, e.g. {"AND": [{"categories": {"in": ["work"]}}]}.',
|
||||||
|
)
|
||||||
.option("--fields <list>", "Specific fields to return (comma-separated).")
|
.option("--fields <list>", "Specific fields to return (comma-separated).")
|
||||||
.option("--show-expired", "Include expired memories.", false)
|
.option("--show-expired", "Include expired memories.", false)
|
||||||
.option(
|
.option(
|
||||||
@@ -404,7 +411,7 @@ program
|
|||||||
.option("--base-url <url>", "Override API base URL.")
|
.option("--base-url <url>", "Override API base URL.")
|
||||||
.addHelpText(
|
.addHelpText(
|
||||||
"after",
|
"after",
|
||||||
'\nExamples:\n $ mem0 search "preferences" --user-id alice\n $ mem0 search "tools" -u alice -o json -k 5\n $ echo "preferences" | mem0 search -u alice',
|
'\nExamples:\n $ mem0 search "preferences" --user-id alice\n $ mem0 search "tools" -u alice -o json -k 5\n $ echo "preferences" | mem0 search -u alice\n $ mem0 search "invoices" -u alice --filter \'{"AND": [{"categories": {"in": ["work"]}}]}\'',
|
||||||
)
|
)
|
||||||
.action(async (query, opts) => {
|
.action(async (query, opts) => {
|
||||||
let resolvedQuery = query;
|
let resolvedQuery = query;
|
||||||
@@ -823,6 +830,13 @@ program
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command("version")
|
||||||
|
.description("Show version and exit.")
|
||||||
|
.action(() => {
|
||||||
|
printVersion();
|
||||||
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command("import <filePath>")
|
.command("import <filePath>")
|
||||||
.description("Import memories from a JSON file.")
|
.description("Import memories from a JSON file.")
|
||||||
|
|||||||
@@ -44,11 +44,17 @@ describe("CLI Integration — help and version", () => {
|
|||||||
expect(result.stdout).toContain("search");
|
expect(result.stdout).toContain("search");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("prints the version with --version, and has no version subcommand", () => {
|
it("prints the version with --version", () => {
|
||||||
const flag = run(["--version"]);
|
const flag = run(["--version"]);
|
||||||
expect(flag.exitCode).toBe(0);
|
expect(flag.exitCode).toBe(0);
|
||||||
expect(flag.stdout).toContain("Mem0");
|
expect(flag.stdout).toContain("Mem0");
|
||||||
expect(run(["version"]).exitCode).not.toBe(0);
|
});
|
||||||
|
|
||||||
|
it("version subcommand output matches --version output byte-for-byte", () => {
|
||||||
|
const flag = run(["--version"]);
|
||||||
|
const cmd = run(["version"]);
|
||||||
|
expect(cmd.exitCode).toBe(0);
|
||||||
|
expect(cmd.stdout).toBe(flag.stdout);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([["help", "--json"], ["--json", "help"], ["--agent", "help"]])(
|
it.each([["help", "--json"], ["--json", "help"], ["--agent", "help"]])(
|
||||||
@@ -129,6 +135,13 @@ describe("CLI Integration — help and version", () => {
|
|||||||
expect(result.stdout).toContain("--rerank");
|
expect(result.stdout).toContain("--rerank");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("search help documents the --filter JSON shape with an example", () => {
|
||||||
|
const result = run(["search", "--help"]);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.stdout).toContain("AND");
|
||||||
|
expect(result.stdout).toContain("categories");
|
||||||
|
});
|
||||||
|
|
||||||
it("list help has --category flag", () => {
|
it("list help has --category flag", () => {
|
||||||
const result = run(["list", "--help"]);
|
const result = run(["list", "--help"]);
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
|||||||
@@ -371,7 +371,11 @@ def search(
|
|||||||
False, "--keyword", help="Use keyword search.", rich_help_panel="Search"
|
False, "--keyword", help="Use keyword search.", rich_help_panel="Search"
|
||||||
),
|
),
|
||||||
filter_json: str | None = typer.Option(
|
filter_json: str | None = typer.Option(
|
||||||
None, "--filter", help="Advanced filter expression (JSON).", rich_help_panel="Search"
|
None,
|
||||||
|
"--filter",
|
||||||
|
help='Advanced filter as JSON: {"AND": [...]} or {"OR": [...]}, '
|
||||||
|
'e.g. {"AND": [{"categories": {"in": ["work"]}}]}.',
|
||||||
|
rich_help_panel="Search",
|
||||||
),
|
),
|
||||||
fields: str | None = typer.Option(
|
fields: str | None = typer.Option(
|
||||||
None,
|
None,
|
||||||
@@ -414,6 +418,7 @@ def search(
|
|||||||
mem0 search "preferences" --user-id alice
|
mem0 search "preferences" --user-id alice
|
||||||
mem0 search "tools" -u alice -o json -k 5
|
mem0 search "tools" -u alice -o json -k 5
|
||||||
echo "preferences" | mem0 search -u alice
|
echo "preferences" | mem0 search -u alice
|
||||||
|
mem0 search "invoices" -u alice --filter '{"AND": [{"categories": {"in": ["work"]}}]}'
|
||||||
"""
|
"""
|
||||||
from mem0_cli.commands.memory import cmd_search
|
from mem0_cli.commands.memory import cmd_search
|
||||||
|
|
||||||
@@ -1084,6 +1089,18 @@ def status(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command(rich_help_panel="Management")
|
||||||
|
def version() -> None:
|
||||||
|
"""Show version and exit.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
mem0 version
|
||||||
|
"""
|
||||||
|
from mem0_cli.commands.utils import cmd_version
|
||||||
|
|
||||||
|
cmd_version()
|
||||||
|
|
||||||
|
|
||||||
@app.command("import", rich_help_panel="Management")
|
@app.command("import", rich_help_panel="Management")
|
||||||
def import_cmd(
|
def import_cmd(
|
||||||
file_path: str = typer.Argument(..., help="JSON file to import."),
|
file_path: str = typer.Argument(..., help="JSON file to import."),
|
||||||
@@ -1165,7 +1182,10 @@ def _build_help_json() -> dict:
|
|||||||
"--threshold": "Minimum similarity score (default: 0.3).",
|
"--threshold": "Minimum similarity score (default: 0.3).",
|
||||||
"--rerank": "Enable reranking (Platform only).",
|
"--rerank": "Enable reranking (Platform only).",
|
||||||
"--keyword": "Use keyword search instead of semantic.",
|
"--keyword": "Use keyword search instead of semantic.",
|
||||||
"--filter": "Advanced filter expression (JSON).",
|
"--filter": (
|
||||||
|
'Advanced filter as JSON: {"AND": [...]} or {"OR": [...]}, '
|
||||||
|
'e.g. {"AND": [{"categories": {"in": ["work"]}}]}.'
|
||||||
|
),
|
||||||
"--fields": "Specific fields to return (comma-separated).",
|
"--fields": "Specific fields to return (comma-separated).",
|
||||||
"--show-expired": "Include expired memories.",
|
"--show-expired": "Include expired memories.",
|
||||||
"--reference-date": "Reference date for relative queries (YYYY-MM-DD or unix timestamp).",
|
"--reference-date": "Reference date for relative queries (YYYY-MM-DD or unix timestamp).",
|
||||||
|
|||||||
@@ -91,7 +91,12 @@ class TestCLIIntegration:
|
|||||||
flag = _run(["--version"])
|
flag = _run(["--version"])
|
||||||
assert flag.returncode == 0
|
assert flag.returncode == 0
|
||||||
assert __version__ in flag.stdout
|
assert __version__ in flag.stdout
|
||||||
assert _run(["version"]).returncode != 0
|
|
||||||
|
def test_version_subcommand_matches_flag_byte_for_byte(self):
|
||||||
|
flag = _run(["--version"])
|
||||||
|
cmd = _run(["version"])
|
||||||
|
assert cmd.returncode == 0
|
||||||
|
assert cmd.stdout == flag.stdout
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"args",
|
"args",
|
||||||
@@ -127,6 +132,12 @@ class TestCLIIntegration:
|
|||||||
assert result.returncode == 0
|
assert result.returncode == 0
|
||||||
assert "top-k" in result.stdout
|
assert "top-k" in result.stdout
|
||||||
|
|
||||||
|
def test_search_help_documents_filter_json_shape(self):
|
||||||
|
result = _run(["search", "--help"])
|
||||||
|
assert result.returncode == 0
|
||||||
|
assert "AND" in result.stdout
|
||||||
|
assert "categories" in result.stdout
|
||||||
|
|
||||||
def test_list_help(self):
|
def test_list_help(self):
|
||||||
result = _run(["list", "--help"])
|
result = _run(["list", "--help"])
|
||||||
assert result.returncode == 0
|
assert result.returncode == 0
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ Search memories using natural language.
|
|||||||
```bash
|
```bash
|
||||||
mem0 search "dietary restrictions" --user-id alice
|
mem0 search "dietary restrictions" --user-id alice
|
||||||
mem0 search "preferred tools" --user-id alice --output json --top-k 5
|
mem0 search "preferred tools" --user-id alice --output json --top-k 5
|
||||||
|
mem0 search "invoices" --user-id alice --filter '{"AND": [{"categories": {"in": ["work"]}}]}'
|
||||||
```
|
```
|
||||||
|
|
||||||
| Flag | Description |
|
| Flag | Description |
|
||||||
@@ -155,7 +156,7 @@ mem0 search "preferred tools" --user-id alice --output json --top-k 5
|
|||||||
| `--threshold` | Minimum similarity score (default: 0.3) |
|
| `--threshold` | Minimum similarity score (default: 0.3) |
|
||||||
| `--rerank` | Enable reranking |
|
| `--rerank` | Enable reranking |
|
||||||
| `--keyword` | Use keyword search instead of semantic |
|
| `--keyword` | Use keyword search instead of semantic |
|
||||||
| `--filter` | Advanced filter expression (JSON) |
|
| `--filter` | Advanced filter as JSON: `{"AND": [...]}` or `{"OR": [...]}`, e.g. `{"AND": [{"categories": {"in": ["work"]}}]}` |
|
||||||
| `--fields` | Return only the named fields |
|
| `--fields` | Return only the named fields |
|
||||||
| `--show-expired` | Include expired memories |
|
| `--show-expired` | Include expired memories |
|
||||||
| `--reference-date` | Reference date for relative queries (`YYYY-MM-DD` or Unix timestamp) |
|
| `--reference-date` | Reference date for relative queries (`YYYY-MM-DD` or Unix timestamp) |
|
||||||
@@ -471,7 +472,7 @@ These two flags belong to `mem0` itself, so they go **before** the command name:
|
|||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `--json` | Enable agent mode: structured JSON envelope output, no colors or spinners |
|
| `--json` | Enable agent mode: structured JSON envelope output, no colors or spinners |
|
||||||
| `--agent` | Alias for `--json` |
|
| `--agent` | Alias for `--json` |
|
||||||
| `--version` | Print the CLI version and exit |
|
| `--version` | Print the CLI version and exit. `mem0 version` does the same thing as a regular subcommand |
|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
On `init` only, `--agent` means something different. `mem0 init --agent` creates an Agent Mode account (see [Sign up as an agent](/platform/agent-signup)); it does not switch the output to JSON. To get JSON from `init`, put the flag first: `mem0 --json init`.
|
On `init` only, `--agent` means something different. `mem0 init --agent` creates an Agent Mode account (see [Sign up as an agent](/platform/agent-signup)); it does not switch the output to JSON. To get JSON from `init`, put the flag first: `mem0 --json init`.
|
||||||
|
|||||||
Reference in New Issue
Block a user