Skip to main content
@3ngram/cli is a thin command-line client over @3ngram/sdk and the REST /api/v1 surface. It is workspace-only before v1.0; the examples use the eventual 3ngram bin name, while local source runs can use node apps/cli/dist/index.js after building the package.
The CLI uses API keys, not MCP OAuth. Pass --api-key or set THREENGRAM_API_KEY; the key is sent as X-API-Key and is never printed in error output.

Configuration

Flags override environment variables. --base-url expects the REST origin without /api/v1.
NameDescription
--base-url <url>REST origin, without /api/v1. Overrides THREENGRAM_BASE_URL.
--api-key <key>API key sent as X-API-Key. Overrides THREENGRAM_API_KEY.
--jsonPrint the raw SDK response instead of human-readable output.
THREENGRAM_BASE_URLDefault REST origin when —base-url is omitted.
THREENGRAM_API_KEYDefault API key when —api-key is omitted.

Commands

remember

Append a typed memory through the REST-backed SDK client. Usage: 3ngram remember --type <t> --topic <t> --content <c> [--scope --project --tags]
SDK call: client.remember(input)

Options

OptionRequiredRepeatableDescription
--type &lt;memory-type>yesnoMemory type: decision, commitment, blocker, fact, preference, pattern, note, or event.
--topic &lt;topic>yesnoShort title for the memory.
--content &lt;text>yesnoMemory body to persist.
--scope &lt;scope>nonoOptional scope such as personal or work.
--project &lt;project>nonoOptional project key for project briefings.
--tags &lt;tag[,tag]>noyesRepeatable; comma-separated values are flattened and trimmed.

Examples

3ngram remember --type decision --topic "search backend" --content "Use Postgres full-text search for v1." --scope work --project 3ngram
3ngram remember --type note --topic "follow-up" --content "Check docs freshness gate." --tags docs,ci --json

Input payload schema

{
  "type": "object",
  "properties": {
    "memoryType": {
      "type": "string",
      "enum": [
        "decision",
        "commitment",
        "blocker",
        "fact",
        "preference",
        "pattern",
        "note",
        "event"
      ]
    },
    "topic": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    },
    "content": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2000
    },
    "scope": {
      "default": "personal",
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^[a-z0-9][a-z0-9-]*$"
    },
    "project": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    },
    "tags": {
      "default": [],
      "maxItems": 32,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64
      }
    }
  },
  "required": [
    "memoryType",
    "topic",
    "content"
  ],
  "additionalProperties": false
}

Output schema

{
  "type": "object",
  "properties": {
    "memory": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid",
          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
        },
        "memoryType": {
          "type": "string",
          "enum": [
            "decision",
            "commitment",
            "blocker",
            "fact",
            "preference",
            "pattern",
            "note",
            "event"
          ]
        },
        "topic": {
          "type": "string"
        },
        "scope": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "^[a-z0-9][a-z0-9-]*$"
        },
        "project": {
          "anyOf": [
            {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "memoryType",
        "topic",
        "scope",
        "project"
      ],
      "additionalProperties": false
    },
    "embedded": {
      "type": "string",
      "enum": [
        "pending",
        "done",
        "failed",
        "off"
      ]
    },
    "commitmentId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    }
  },
  "required": [
    "memory",
    "embedded"
  ],
  "additionalProperties": false
}
Search memories with optional filters before result fusion. Usage: 3ngram search <query> [--limit --type --scope --project --status]
SDK call: client.search(query, options)

Options

OptionRequiredRepeatableDescription
&lt;query>yesnoPositional search text. Quote multi-word queries.
--query &lt;query>nonoAlternative to the positional query; wins when both are present.
--limit &lt;n>nonoMaximum result count.
--type &lt;memory-type>nonoMaps to the SDK memoryType filter.
--scope &lt;scope>nonoRestrict search to one scope.
--project &lt;project>nonoRestrict search to one project.
--status &lt;active|archived>nonoFilter by supersession-aware memory status.

Examples

3ngram search "oauth refresh token" --scope work --project 3ngram --limit 5
3ngram search --query "design decision" --type decision --json

Input payload schema

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "minLength": 1
    },
    "limit": {
      "default": 5,
      "type": "integer",
      "minimum": 1,
      "maximum": 25
    },
    "memoryType": {
      "type": "string",
      "enum": [
        "decision",
        "commitment",
        "blocker",
        "fact",
        "preference",
        "pattern",
        "note",
        "event"
      ]
    },
    "scope": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^[a-z0-9][a-z0-9-]*$"
    },
    "project": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    },
    "status": {
      "type": "string",
      "enum": [
        "active",
        "archived"
      ]
    }
  },
  "required": [
    "query"
  ],
  "additionalProperties": false
}

Output schema

{
  "type": "object",
  "properties": {
    "hits": {
      "maxItems": 25,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "memoryType": {
            "type": "string"
          },
          "topic": {
            "type": "string"
          },
          "content": {
            "type": "string",
            "maxLength": 600
          },
          "contentLength": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "truncated": {
            "type": "boolean"
          },
          "score": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "memoryType",
          "topic",
          "content",
          "contentLength",
          "truncated",
          "score"
        ],
        "additionalProperties": false
      }
    },
    "count": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "hits",
    "count"
  ],
  "additionalProperties": false
}

facts

Read currently-valid facts, optionally with bi-temporal filters. Usage: 3ngram facts [--subject --predicate --valid-at --as-known-at --limit]
SDK call: client.getFacts(filters)

Options

OptionRequiredRepeatableDescription
--subject &lt;subject>nonoRestrict facts to one subject.
--predicate &lt;predicate>nonoRestrict facts to one predicate.
--valid-at &lt;iso-date>nonoWorld-time coordinate for bi-temporal lookup.
--as-known-at &lt;iso-date>nonoKnowledge-time coordinate for bi-temporal lookup.
--limit &lt;n>nonoMaximum fact count.

Examples

3ngram facts --subject 3ngram --predicate status
3ngram facts --subject "search backend" --valid-at 2026-06-01T00:00:00.000Z --json

Input payload schema

{
  "type": "object",
  "properties": {
    "subject": {
      "type": "string",
      "minLength": 1
    },
    "predicate": {
      "type": "string",
      "minLength": 1
    },
    "asOf": {
      "type": "object",
      "properties": {
        "validAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
        },
        "asKnownAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
        }
      },
      "additionalProperties": false
    },
    "limit": {
      "default": 50,
      "type": "integer",
      "minimum": 1,
      "maximum": 200
    }
  },
  "additionalProperties": false
}

Output schema

{
  "type": "object",
  "properties": {
    "facts": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "subject": {
            "type": "string"
          },
          "predicate": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "confidence": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "validFrom": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
          },
          "validTo": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "subject",
          "predicate",
          "value",
          "confidence",
          "validFrom",
          "validTo"
        ],
        "additionalProperties": false
      }
    },
    "count": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "facts",
    "count"
  ],
  "additionalProperties": false
}

Exit behavior

  • 0: command completed successfully.
  • 1: no command was supplied, the server rejected the request, or the transport failed.
  • 2: CLI usage or configuration error, including missing required flags.