Skip to main content

sqlite_graphrag/commands/remember/
args.rs

1//! CLI arguments for the `remember` command.
2
3use crate::cli::MemoryType;
4use crate::output::JsonOutputFormat;
5
6#[derive(clap::Args)]
7#[command(after_long_help = "EXAMPLES:\n  \
8    # Create a memory with inline body\n  \
9    sqlite-graphrag remember --name design-auth --type decision \\\n    \
10    --description \"auth design\" --body \"JWT for stateless auth\"\n\n  \
11    # Create with curated graph via --graph-stdin\n  \
12    echo '{\"body\":\"...\",\"entities\":[],\"relationships\":[]}' | \\\n    \
13    sqlite-graphrag remember --name my-mem --type note --description \"desc\" --graph-stdin\n\n  \
14    # Enable automatic URL extraction with --graph-stdin (URL-regex only since v1.0.79)\n  \
15    echo '{\"body\":\"See https://docs.rs ...\",\"entities\":[],\"relationships\":[]}' | \\\n    \
16    sqlite-graphrag remember --name url-test --type note --description \"test\" \\\n    \
17    --graph-stdin --enable-ner\n\n  \
18    # Idempotent upsert with --force-merge\n  \
19    sqlite-graphrag remember --name my-mem --type note --description \"updated\" \\\n    \
20    --body \"new content\" --force-merge\n\n\
21NOTE:\n  \
22    remember does NOT accept positional arguments.\n  \
23    Use --body \"text\" for inline content\n  \
24    Use --body-file path for file content\n  \
25    Use --body-stdin for piped content\n  \
26    Use --graph-stdin for JSON with entities and relationships\n\n\
27ENTITY TYPES (for --graph-stdin entities, NOT memory --type):\n  \
28    concept, tool, person, file, project, decision, incident,\n  \
29    organization, location, date, dashboard, issue_tracker, memory\n  \
30    WARNING: reference, skill, document, note, user, feedback are\n  \
31    MEMORY types only — NOT valid for entities.\n  \
32    Mapping: reference→concept, document→file, user→person")]
33/// Remember args.
34pub struct RememberArgs {
35    /// Memory name in kebab-case (lowercase letters, digits, hyphens).
36    /// Acts as unique key within the namespace; collisions trigger merge or rejection.
37    #[arg(long)]
38    pub name: String,
39    #[arg(
40        long,
41        value_enum,
42        long_help = "Memory kind stored in `memories.type`. Required when creating a new memory. Optional with --force-merge: if omitted the existing memory type is inherited. This is NOT the graph `entity_type` used in `--entities-file`. Valid values: user, feedback, project, reference, decision, incident, skill, document, note."
43    )]
44    /// Item.
45    pub r#type: Option<MemoryType>,
46    /// Short description (≤500 chars) summarizing the memory for use in `list` and `recall` snippets.
47    /// Required when creating a new memory. Optional with --force-merge: if omitted the existing description is inherited.
48    ///
49    /// GAP-SG-33: `allow_hyphen_values` lets a description that begins with a
50    /// hyphen (e.g. `"- bullet"`) be accepted as a value instead of being
51    /// mistaken for a flag.
52    #[arg(long, allow_hyphen_values = true)]
53    pub description: Option<String>,
54    /// Inline body content. Mutually exclusive with --body-file, --body-stdin, --graph-stdin.
55    /// Maximum 512000 bytes; rejected if empty without an external graph.
56    ///
57    /// GAP-SG-33: `allow_hyphen_values` lets a body that begins with a hyphen
58    /// (e.g. a markdown bullet list) be accepted as a value.
59    #[arg(
60        long,
61        allow_hyphen_values = true,
62        help = "Inline body content (max 500 KB / 512000 bytes and 30000 estimated tokens; split dense bodies into multiple memories at ~25000 tokens, or use --body-file)",
63        conflicts_with_all = ["body_file", "body_stdin", "graph_stdin"]
64    )]
65    pub body: Option<String>,
66    #[arg(
67        long,
68        help = "Read body from a file instead of --body",
69        conflicts_with_all = ["body", "body_stdin", "graph_stdin"]
70    )]
71    /// Body file.
72    pub body_file: Option<std::path::PathBuf>,
73    /// Read body from stdin until EOF. Useful in pipes (echo "..." | sqlite-graphrag remember ...).
74    /// Mutually exclusive with --body, --body-file, --graph-stdin.
75    #[arg(
76        long,
77        conflicts_with_all = ["body", "body_file", "graph_stdin"]
78    )]
79    pub body_stdin: bool,
80    #[arg(
81        long,
82        help = "JSON file containing entities to associate with this memory"
83    )]
84    /// Entities file.
85    pub entities_file: Option<std::path::PathBuf>,
86    #[arg(
87        long,
88        help = "JSON file containing relationships to associate with this memory"
89    )]
90    /// Relationships file.
91    pub relationships_file: Option<std::path::PathBuf>,
92    #[arg(
93        long,
94        help = "Read graph JSON (body + entities + relationships) from stdin",
95        conflicts_with_all = [
96            "body",
97            "body_file",
98            "body_stdin",
99            "entities_file",
100            "relationships_file",
101            "graph_file"
102        ]
103    )]
104    /// Graph stdin.
105    pub graph_stdin: bool,
106    /// GAP-SG-30: read graph JSON (`{body, entities, relationships}`) from a
107    /// FILE instead of stdin, so a curated graph can combine with a body
108    /// supplied via --body / --body-file / --body-stdin (which previously
109    /// conflicted with --graph-stdin over the single stdin). The file's `body`
110    /// field is used only when no other body source is given; otherwise the
111    /// body source wins and only the file's entities/relationships are applied.
112    #[arg(
113        long,
114        value_name = "PATH",
115        help = "Read graph JSON (body + entities + relationships) from a file (combines with --body/--body-file/--body-stdin)",
116        conflicts_with_all = ["graph_stdin", "entities_file", "relationships_file"]
117    )]
118    pub graph_file: Option<std::path::PathBuf>,
119    #[arg(
120        long,
121        help = "Namespace (flag / XDG namespace.default / global)"
122    )]
123    /// Namespace scope.
124    pub namespace: Option<String>,
125    /// Inline JSON object with arbitrary metadata key-value pairs. Mutually exclusive with --metadata-file.
126    #[arg(long)]
127    pub metadata: Option<String>,
128    /// Metadata file.
129    #[arg(long, help = "JSON file containing metadata key-value pairs")]
130    pub metadata_file: Option<std::path::PathBuf>,
131    /// Force merge.
132    #[arg(long)]
133    pub force_merge: bool,
134    #[arg(
135        long,
136        value_name = "EPOCH_OR_RFC3339",
137        value_parser = crate::parsers::parse_expected_updated_at,
138        long_help = "Optimistic lock: reject if updated_at does not match. \
139Accepts Unix epoch (e.g. 1700000000) or RFC 3339 (e.g. 2026-04-19T12:00:00Z)."
140    )]
141    /// Expected updated at.
142    pub expected_updated_at: Option<i64>,
143    #[arg(
144        long,
145                value_parser = crate::parsers::parse_bool_flexible,
146        action = clap::ArgAction::Set,
147        num_args = 0..=1,
148        default_missing_value = "true",
149        default_value = "false",
150        help = "Enable automatic URL-regex extraction from body (URL-regex only since v1.0.79)"
151    )]
152    /// Enable NER.
153    pub enable_ner: bool,
154    /// Skip extraction.
155    #[arg(long, hide = true)]
156    pub skip_extraction: bool,
157    /// Explicitly clear the body content (set to empty string). Required to distinguish
158    /// intentional body clearing from accidental omission during --force-merge.
159    /// Without this flag, an empty body passed to --force-merge preserves the existing body.
160    #[arg(
161        long,
162        default_value_t = false,
163        help = "Explicitly clear body content during --force-merge (without this flag, an empty body is ignored and the existing body is kept)"
164    )]
165    pub clear_body: bool,
166    /// Validate input and report planned actions without persisting.
167    #[arg(
168        long,
169        default_value_t = false,
170        help = "Validate input and report planned actions without persisting"
171    )]
172    pub dry_run: bool,
173    /// GAP-SG-37: reject (instead of silently normalizing) when the supplied
174    /// --name is not already canonical kebab-case. Use this when the literal
175    /// name matters and a silent transform would surprise downstream lookups.
176    #[arg(
177        long,
178        default_value_t = false,
179        help = "Reject the write if --name would be normalized to kebab-case (preserve-name guard)"
180    )]
181    pub strict_name: bool,
182    /// GAP-SG-51: with --force-merge, REPLACE the memory's entity/relationship
183    /// bindings with the supplied set instead of merging additively. Combined
184    /// with an empty `entities`/`relationships` payload this clears all bindings
185    /// without deleting the memory.
186    #[arg(
187        long,
188        default_value_t = false,
189        help = "With --force-merge, replace (not merge) the memory's graph bindings; empty entities clears them"
190    )]
191    pub replace_graph: bool,
192    /// Optional opaque session identifier for tracing memory provenance across multi-agent runs.
193    #[arg(long)]
194    pub session_id: Option<String>,
195    /// Output format.
196    #[arg(long, value_enum, default_value_t = JsonOutputFormat::Json)]
197    pub format: JsonOutputFormat,
198    /// Emit machine-readable JSON on stdout.
199    #[arg(long, hide = true, help = "No-op; JSON is always emitted on stdout")]
200    pub json: bool,
201    /// Path to the SQLite database file.
202    #[arg(long)]
203    pub db: Option<String>,
204    /// Maximum process RSS in MiB; abort if exceeded during embedding.
205    #[arg(long, default_value_t = crate::constants::DEFAULT_MAX_RSS_MB,
206          help = "Maximum process RSS in MiB; abort if exceeded during embedding (default: 8192)")]
207    pub max_rss_mb: u64,
208    /// G42/S3 (v1.0.79): maximum simultaneous LLM embedding subprocesses.
209    /// The effective value is further bounded by CPU count and available
210    /// RAM (permits = min(N, cpus, ram_livre*0.5/350MB), clamp [1, 32]).
211    #[arg(long, default_value_t = 4, value_name = "N",
212          value_parser = clap::value_parser!(u64).range(1..=32),
213          help = "Maximum simultaneous LLM embedding subprocesses (default: 4, clamp [1,32])")]
214    pub llm_parallelism: u64,
215    /// GAP-CLI-PRIO-02: after write, enqueue entity-descriptions for the
216    /// entities linked in this call (priority hot set). Default false —
217    /// operators enable when they want automatic priority enrich.
218    #[arg(long, default_value_t = false)]
219    pub enqueue_enrich: bool,
220}