1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
use std::path::PathBuf;
use clap::{Args, ValueEnum};
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum StatusLane {
/// Show both local beta and production GA boundaries.
All,
/// Emphasize the local/design-partner beta lane.
LocalBeta,
/// Emphasize the production GA lane.
ProductionGa,
}
impl StatusLane {
pub(crate) const fn as_str(self) -> &'static str {
match self {
Self::All => "all",
Self::LocalBeta => "local-beta",
Self::ProductionGa => "production-ga",
}
}
}
#[derive(Args)]
pub(crate) struct FixCliArgs {
/// Apply all high-confidence suggestions without prompting.
#[arg(long)]
pub(crate) yes: bool,
/// Diff scope: `staged`, `worktree`, or `all` (auto-detect; default).
#[arg(long, value_name = "SCOPE")]
pub(crate) diff: Option<String>,
/// Print which recalled memories produced each finding.
#[arg(long, hide = true)]
pub(crate) explain_rules: bool,
/// Emit a Markdown report. Pass a file path, or `-` for stdout.
#[arg(long, value_name = "PATH", hide = true)]
pub(crate) report: Option<String>,
/// Output applied-fix result as JSON. Requires `--yes`.
#[arg(long, requires = "yes")]
pub(crate) json: bool,
/// Fix a GitHub PR locally. Accepts number, owner/repo#number, or PR URL.
#[arg(long, value_name = "PR", conflicts_with = "diff")]
pub(crate) pr: Option<String>,
/// Checkout PR mode into this local branch name.
#[arg(
long,
value_name = "NAME",
requires = "pr",
conflicts_with = "no_checkout",
hide = true
)]
pub(crate) work_branch: Option<String>,
/// In PR mode, analyze the current checkout instead of switching branches.
#[arg(long, requires = "pr", hide = true)]
pub(crate) no_checkout: bool,
/// In PR mode, allow running with local uncommitted changes.
#[arg(long, requires = "pr", hide = true)]
pub(crate) allow_dirty: bool,
/// In PR mode, skip uploading accepted-fix proof for local rehearsals.
#[arg(long, requires = "pr", hide = true)]
pub(crate) no_upload_acceptance: bool,
/// Path to fix. Defaults to staged changes, then working tree.
pub(crate) path: Option<PathBuf>,
}
#[derive(Args)]
pub(crate) struct ReviewCliArgs {
/// Machine gate: exits 1 on high-confidence actionable findings. Never modifies files.
#[arg(long)]
pub(crate) ci: bool,
/// With `--ci`, also fail on low-confidence suggestions. Requires `--ci`.
#[arg(long, requires = "ci")]
pub(crate) strict: bool,
/// Diff scope: `staged`, `worktree`, or `all` (auto-detect; default).
#[arg(long, value_name = "SCOPE")]
pub(crate) diff: Option<String>,
/// Print which recalled memories produced each finding.
#[arg(long, hide = true)]
pub(crate) explain_rules: bool,
/// Emit a Markdown report. Pass a file path, or `-` for stdout.
#[arg(long, value_name = "PATH", hide = true)]
pub(crate) report: Option<String>,
/// Output as JSON.
#[arg(long)]
pub(crate) json: bool,
/// Review a GitHub PR. Accepts number, owner/repo#number, or PR URL.
#[arg(long, value_name = "PR", conflicts_with = "diff")]
pub(crate) pr: Option<String>,
/// In PR mode, analyze the current checkout instead of switching branches.
#[arg(long, requires = "pr", hide = true)]
pub(crate) no_checkout: bool,
/// In PR mode, allow running with local uncommitted changes.
#[arg(long, requires = "pr", hide = true)]
pub(crate) allow_dirty: bool,
/// Path to review. Defaults to staged changes, then working tree.
pub(crate) path: Option<PathBuf>,
}
#[derive(Args)]
pub(crate) struct SyncCliArgs {
/// Pull-only: download cloud changes without pushing local edits.
#[arg(long, conflicts_with = "push")]
pub(crate) pull: bool,
/// Push-only: upload local changes without pulling cloud updates.
#[arg(long, conflicts_with = "pull")]
pub(crate) push: bool,
/// Show what would change without writing anywhere.
#[arg(long)]
pub(crate) dry_run: bool,
/// Also upload raw observation_events activity. Skipped by default.
#[arg(long)]
pub(crate) include_observations: bool,
/// Also upload raw session-mined memory candidates. Skipped by default.
#[arg(long)]
pub(crate) include_candidates: bool,
/// Also upload raw imported-review, review-metric, and trajectory telemetry. Skipped by default.
#[arg(long)]
pub(crate) include_telemetry: bool,
/// Output as JSON.
#[arg(long)]
pub(crate) json: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum ImportProviderArg {
/// Import PR review history via the GitHub CLI (`gh`).
Github,
/// Import MR discussions via the GitLab REST API (PAT from `difflore auth gitlab`).
Gitlab,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum ImportDistillArg {
/// Use deterministic local heuristics.
Heuristic,
/// Ask an installed local agent CLI to distill pending candidates.
LocalAgent,
}
#[derive(Args)]
pub(crate) struct ImportReviewsCliArgs {
/// Repo to import. Uses git remote when omitted.
#[arg(long)]
pub(crate) repo: Option<String>,
/// Import from an upstream GitHub repo and attach memory to this repo.
#[arg(long, value_name = "OWNER/REPO")]
pub(crate) from_upstream: Option<String>,
/// Review provider. Uses git remote detection when omitted.
#[arg(long, value_enum, value_name = "PROVIDER")]
pub(crate) provider: Option<ImportProviderArg>,
/// Self-managed GitLab host (e.g. gitlab.corp.example). Implies `--provider gitlab`.
#[arg(long, value_name = "HOST")]
pub(crate) gitlab_host: Option<String>,
/// Maximum number of PRs (GitLab: MRs) to import.
#[arg(long, default_value_t = 50)]
pub(crate) max_prs: usize,
/// Import one PR number (GitLab: MR IID). Repeat for multiple.
#[arg(long = "pr", value_name = "NUMBER")]
pub(crate) pr_numbers: Vec<i32>,
/// Comma-separated PR numbers to exclude from import.
#[arg(long = "exclude-prs", value_name = "CSV", value_delimiter = ',')]
pub(crate) exclude_prs: Vec<i32>,
/// Only import PRs merged after this date (YYYY-MM-DD).
#[arg(long)]
pub(crate) since: Option<String>,
/// Also include open PRs (default imports merged PRs only).
#[arg(long)]
pub(crate) include_open: bool,
/// Upload imported reviews for cloud extraction instead of local drafting.
#[arg(long)]
pub(crate) upload: bool,
/// Local distillation strategy when not uploading.
#[arg(
long,
value_enum,
default_value_t = ImportDistillArg::Heuristic,
conflicts_with = "upload"
)]
pub(crate) distill: ImportDistillArg,
/// Preview what would be imported without writing or uploading.
#[arg(long)]
pub(crate) dry_run: bool,
/// Output as JSON.
#[arg(long)]
pub(crate) json: bool,
/// Internal wall-clock cap for bounded background imports.
#[arg(long, hide = true, value_name = "SECONDS")]
pub(crate) wall_timeout_secs: Option<u64>,
}
#[derive(Args)]
pub(crate) struct LearnCliArgs {
/// Optional note to capture alongside the recent session.
#[arg(long)]
pub(crate) note: Option<String>,
/// Explicit transcript JSONL path. Defaults to the newest Claude Code transcript.
#[arg(long, value_name = "PATH")]
pub(crate) transcript: Option<PathBuf>,
/// Explicit session id. Defaults to the transcript file stem.
#[arg(long, value_name = "ID")]
pub(crate) session: Option<String>,
/// Agent transcript format/client name.
#[arg(long, default_value = "claude-code", hide = true)]
pub(crate) client: String,
/// Output as JSON.
#[arg(long)]
pub(crate) json: bool,
}
#[derive(Args)]
pub(crate) struct InitCliArgs {
/// Readiness preview only — never wires agents or runs provider setup.
#[arg(long)]
pub(crate) check: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum ExportFormatArg {
/// `AGENTS.md` at the repo root (cross-agent convention; no engine filter).
AgentsMd,
/// `CLAUDE.md` at the repo root (only rules enabled for the claude engine).
ClaudeMd,
/// Both emitters.
All,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum MemoryPackageFormatArg {
/// Infer from --output: .json writes one JSON file; otherwise writes a Markdown directory.
Auto,
/// Write one versioned JSON package file.
Json,
/// Write a directory package with manifest.json plus editable Markdown rule files.
Markdown,
}
#[derive(Args)]
pub(crate) struct ExportCliArgs {
/// Target format(s): `agents-md`, `claude-md`, or `all`. Repeatable.
#[arg(long, value_enum, value_name = "FORMAT", default_values_t = [ExportFormatArg::All])]
pub(crate) format: Vec<ExportFormatArg>,
/// Print the export plan (create/update/unchanged/skipped) without writing.
#[arg(long)]
pub(crate) dry_run: bool,
/// Output as JSON.
#[arg(long)]
pub(crate) json: bool,
/// Skip Bad/Good example blocks to keep the export small.
#[arg(long)]
pub(crate) no_examples: bool,
/// Export local rules only; exclude team/cloud-synced rules.
#[arg(long)]
pub(crate) local_only: bool,
/// Cap export to the first N rules. Unlimited when omitted.
#[arg(long, value_name = "N", value_parser = clap::value_parser!(u64).range(1..))]
pub(crate) max_rules: Option<u64>,
}
#[derive(Args)]
pub(crate) struct RecallCliArgs {
/// Recall intent text. Optional when `--diff` is set.
pub(crate) intent: Option<String>,
/// File path that would be edited (drives the `file_pattern` cascade).
#[arg(long, value_name = "PATH")]
pub(crate) file: Option<String>,
/// Infer files (and rough intent) from the current `git diff --name-only`.
#[arg(long)]
pub(crate) diff: bool,
/// Number of rules to return (1..=50).
#[arg(long, default_value_t = 5)]
pub(crate) top_k: usize,
/// Output as JSON.
#[arg(long)]
pub(crate) json: bool,
/// Show each rule's `file_patterns` and `source_repo`.
#[arg(long)]
pub(crate) verbose: bool,
/// Print paste-ready Markdown; conflicts with `--json`.
#[arg(long, conflicts_with = "json")]
pub(crate) copy: bool,
}