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/// Remember args.
8///
9/// GAP-SG-216: this struct carried an `after_long_help` block from v1.0.x until
10/// v1.2.8 and NONE of it ever reached a terminal. `Commands::Remember` in
11/// `crate::cli::commands` declares the same attribute on the enum variant, and
12/// clap keeps that one — silently, with no warning and no lint. The dead block
13/// is where the false claim "reference, skill, document, note, user, feedback
14/// are MEMORY types only — NOT valid for entities" survived two minor releases
15/// after v1.1.8 made the parser accept all six.
16///
17/// The authoritative block now lives beside the variant, where it renders, and
18/// `tests/remember_input_contract_gate.rs` reads THAT text. Do not add a second
19/// `after_long_help` here: a help attribute nothing renders is prose that can
20/// only rot.
21pub struct RememberArgs {
22 /// GAP-SG-216: memory name as a positional argument, the same form `edit`,
23 /// `read`, `forget`, `history`, `related`, `rename`, `restore` and
24 /// `memory-entities` already accept. `remember` alone refused it, and the
25 /// refusal arrived as clap's generic `unexpected argument`, which names
26 /// neither `--name` nor the reason.
27 #[arg(
28 value_name = "NAME",
29 conflicts_with = "name",
30 help = "Memory name in kebab-case; alternative to --name"
31 )]
32 pub name_positional: Option<String>,
33 /// Memory name in kebab-case (lowercase letters, digits, hyphens).
34 /// Acts as unique key within the namespace; collisions trigger merge or rejection.
35 ///
36 /// Optional at the clap layer only because the positional form is the
37 /// alternative; `super::name::resolve` refuses when NEITHER is given.
38 #[arg(long)]
39 pub name: Option<String>,
40 #[arg(
41 long,
42 value_enum,
43 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."
44 )]
45 /// Item.
46 pub r#type: Option<MemoryType>,
47 /// Short description (≤500 chars) summarizing the memory for use in `list` and `recall` snippets.
48 /// Required when creating a new memory. Optional with --force-merge: if omitted the existing description is inherited.
49 ///
50 /// GAP-SG-33: `allow_hyphen_values` lets a description that begins with a
51 /// hyphen (e.g. `"- bullet"`) be accepted as a value instead of being
52 /// mistaken for a flag.
53 #[arg(long, allow_hyphen_values = true)]
54 pub description: Option<String>,
55 /// Inline body content. Mutually exclusive with --body-file, --body-stdin, --graph-stdin.
56 /// Maximum 512000 bytes; rejected if empty without an external graph.
57 ///
58 /// GAP-SG-33: `allow_hyphen_values` lets a body that begins with a hyphen
59 /// (e.g. a markdown bullet list) be accepted as a value.
60 #[arg(
61 long,
62 allow_hyphen_values = true,
63 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)",
64 conflicts_with_all = ["body_file", "body_stdin", "graph_stdin"]
65 )]
66 pub body: Option<String>,
67 #[arg(
68 long,
69 help = "Read body from a file instead of --body",
70 conflicts_with_all = ["body", "body_stdin", "graph_stdin"]
71 )]
72 /// Body file.
73 pub body_file: Option<std::path::PathBuf>,
74 /// Read body from stdin until EOF. Useful in pipes (echo "..." | sqlite-graphrag remember ...).
75 /// Mutually exclusive with --body, --body-file, --graph-stdin.
76 #[arg(
77 long,
78 conflicts_with_all = ["body", "body_file", "graph_stdin"]
79 )]
80 pub body_stdin: bool,
81 #[arg(
82 long,
83 help = "JSON file containing entities to associate with this memory"
84 )]
85 /// Entities file.
86 pub entities_file: Option<std::path::PathBuf>,
87 #[arg(
88 long,
89 help = "JSON file containing relationships to associate with this memory"
90 )]
91 /// Relationships file.
92 pub relationships_file: Option<std::path::PathBuf>,
93 #[arg(
94 long,
95 help = "Read graph JSON (body + entities + relationships) from stdin",
96 conflicts_with_all = [
97 "body",
98 "body_file",
99 "body_stdin",
100 "entities_file",
101 "relationships_file",
102 "graph_file"
103 ]
104 )]
105 /// Graph stdin.
106 pub graph_stdin: bool,
107 /// GAP-SG-30: read graph JSON (`{body, entities, relationships}`) from a
108 /// FILE instead of stdin, so a curated graph can combine with a body
109 /// supplied via --body / --body-file / --body-stdin (which previously
110 /// conflicted with --graph-stdin over the single stdin). The file's `body`
111 /// field is used only when no other body source is given; otherwise the
112 /// body source wins and only the file's entities/relationships are applied.
113 #[arg(
114 long,
115 value_name = "PATH",
116 help = "Read graph JSON (body + entities + relationships) from a file (combines with --body/--body-file/--body-stdin)",
117 conflicts_with_all = ["graph_stdin", "entities_file", "relationships_file"]
118 )]
119 pub graph_file: Option<std::path::PathBuf>,
120 #[arg(long, help = "Namespace (flag / XDG namespace.default / global)")]
121 /// Namespace scope.
122 pub namespace: Option<String>,
123 /// Inline JSON object with arbitrary metadata key-value pairs. Mutually exclusive with --metadata-file.
124 #[arg(long)]
125 pub metadata: Option<String>,
126 /// Metadata file.
127 #[arg(long, help = "JSON file containing metadata key-value pairs")]
128 pub metadata_file: Option<std::path::PathBuf>,
129 /// Force merge.
130 #[arg(long)]
131 pub force_merge: bool,
132 #[arg(
133 long,
134 value_name = "EPOCH_OR_RFC3339",
135 value_parser = crate::parsers::parse_expected_updated_at,
136 long_help = "Optimistic lock: reject if updated_at does not match. \
137Accepts Unix epoch (e.g. 1700000000) or RFC 3339 (e.g. 2026-04-19T12:00:00Z)."
138 )]
139 /// Expected updated at.
140 pub expected_updated_at: Option<i64>,
141 #[arg(
142 long,
143 value_parser = crate::parsers::parse_bool_flexible,
144 action = clap::ArgAction::Set,
145 num_args = 0..=1,
146 default_missing_value = "true",
147 default_value = "false",
148 help = "Enable automatic URL-regex extraction from body (URL-regex only since v1.0.79)"
149 )]
150 /// Enable NER.
151 pub enable_ner: bool,
152 /// Skip extraction.
153 #[arg(long, hide = true)]
154 pub skip_extraction: bool,
155 /// Explicitly clear the body content (set to empty string). Required to distinguish
156 /// intentional body clearing from accidental omission during --force-merge.
157 /// Without this flag, an empty body passed to --force-merge preserves the existing body.
158 #[arg(
159 long,
160 default_value_t = false,
161 help = "Explicitly clear body content during --force-merge (without this flag, an empty body is ignored and the existing body is kept)"
162 )]
163 pub clear_body: bool,
164 /// Validate input and report planned actions without persisting.
165 #[arg(
166 long,
167 default_value_t = false,
168 help = "Validate input and report planned actions without persisting"
169 )]
170 pub dry_run: bool,
171 /// GAP-SG-37: reject (instead of silently normalizing) when the supplied
172 /// --name is not already canonical kebab-case. Use this when the literal
173 /// name matters and a silent transform would surprise downstream lookups.
174 #[arg(
175 long,
176 default_value_t = false,
177 help = "Reject the write if --name would be normalized to kebab-case (preserve-name guard)"
178 )]
179 pub strict_name: bool,
180 /// GAP-SG-216: reject a declared `entity_type` outside the thirteen
181 /// canonical kinds.
182 ///
183 /// The sibling of [`Self::strict_name`]: one field guards the name the
184 /// caller typed, the other the taxonomy. An open vocabulary stays the
185 /// default because LLM extraction depends on it — an extractor cannot be
186 /// asked to emit only thirteen labels, while a caller who typed one can opt
187 /// into being refused.
188 #[arg(
189 long,
190 default_value_t = false,
191 help = "Reject the write if a declared entity_type is outside the canonical vocabulary"
192 )]
193 pub strict_entity_types: bool,
194 /// GAP-SG-51: with --force-merge, REPLACE the memory's entity/relationship
195 /// bindings with the supplied set instead of merging additively. Combined
196 /// with an empty `entities`/`relationships` payload this clears all bindings
197 /// without deleting the memory.
198 #[arg(
199 long,
200 default_value_t = false,
201 help = "With --force-merge, replace (not merge) the memory's graph bindings; empty entities clears them"
202 )]
203 pub replace_graph: bool,
204 /// Optional opaque session identifier for tracing memory provenance across multi-agent runs.
205 #[arg(long)]
206 pub session_id: Option<String>,
207 /// Output format.
208 #[arg(long, value_enum, default_value_t = JsonOutputFormat::Json)]
209 pub format: JsonOutputFormat,
210 /// Emit machine-readable JSON on stdout.
211 #[arg(long, hide = true, help = "No-op; JSON is always emitted on stdout")]
212 pub json: bool,
213 /// Path to the SQLite database file.
214 #[arg(long)]
215 pub db: Option<String>,
216 /// Maximum process RSS in MiB; abort if exceeded during embedding.
217 #[arg(long, default_value_t = crate::constants::DEFAULT_MAX_RSS_MB,
218 help = "Maximum process RSS in MiB; abort if exceeded during embedding (default: 8192)")]
219 pub max_rss_mb: u64,
220 /// G42/S3 (v1.0.79): maximum simultaneous LLM embedding subprocesses.
221 /// The effective value is further bounded by CPU count and available
222 /// RAM (permits = min(N, cpus, ram_livre*0.5/350MB), clamp [1, 32]).
223 #[arg(long, default_value_t = 4, value_name = "N",
224 value_parser = clap::value_parser!(u64).range(1..=32),
225 help = "Maximum simultaneous LLM embedding subprocesses (default: 4, clamp [1,32])")]
226 pub llm_parallelism: u64,
227 /// GAP-CLI-PRIO-02: after write, enqueue entity-descriptions for the
228 /// entities linked in this call (priority hot set). Default false —
229 /// operators enable when they want automatic priority enrich.
230 #[arg(long, default_value_t = false)]
231 pub enqueue_enrich: bool,
232}