morph_cli/cli.rs
1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4#[derive(Debug, Parser)]
5#[command(
6 name = "morph",
7 version,
8 about = "Plugin-based code transformation CLI for migrating codebases",
9 long_about = None,
10 after_help = "✨ GETTING STARTED WITH morph-cli ✨\n\n\
11 1. Scan your project to identify files & technologies:\n\
12 $ morph scan\n\n\
13 2. Plan recommendations & estimate maturity/confidence:\n\
14 $ morph plan\n\n\
15 3. Run the interactive guided assistant wizard:\n\
16 $ morph magic\n\n\
17 🚀 BEGINNER-SAFE RECOMMENDATIONS:\n\n\
18 • Migrate legacy CommonJS require calls to modern ESM imports:\n\
19 $ morph run commonjs-to-esm . --dry-run --review\n\n\
20 • Safely upgrade JavaScript files to TypeScript with complete confidence:\n\
21 $ morph run js-to-ts . --dry-run --review\n\n\
22 • Validate repository setup and environment health:\n\
23 $ morph doctor .\n\n\
24 👉 Need help? Visit our docs or run a preset workflow: `morph preset list`"
25)]
26pub struct Cli {
27 #[command(subcommand)]
28 pub command: Command,
29}
30
31#[derive(Debug, Subcommand)]
32pub enum Command {
33 /// Print the current morph-cli version
34 Version,
35 /// List available migration recipes
36 List {
37 #[arg(default_value = ".")]
38 path: PathBuf,
39 /// Filter recipes by category (e.g. migration, cleanup, modernization, analysis, experimental)
40 #[arg(long)]
41 category: Option<String>,
42 /// Filter recipes by tag (e.g. safe, fast, risky, typescript, react, backend, frontend)
43 #[arg(long)]
44 tag: Option<String>,
45 },
46 /// Search for recipes matching a query
47 Search {
48 /// Search query (matches name, description, category, tags)
49 query: String,
50 #[arg(default_value = ".")]
51 path: PathBuf,
52 /// Filter by category (e.g. migration, cleanup, modernization, analysis, experimental)
53 #[arg(long)]
54 category: Option<String>,
55 /// Filter by maturity (e.g. stable, beta, experimental)
56 #[arg(long)]
57 maturity: Option<String>,
58 },
59 /// Show migration history summaries
60 History {
61 /// Limit the number of sessions to display
62 #[arg(short = 'n', long, default_value_t = 10)]
63 limit: usize,
64 },
65 /// Initialize a new morph-cli.toml configuration file
66 Init {
67 #[arg(default_value = ".")]
68 path: PathBuf,
69 },
70 /// Plan recommended migration recipes for a project
71 #[command(visible_alias = "p")]
72 Plan {
73 #[arg(default_value = ".")]
74 path: PathBuf,
75 /// Filter by file tag (e.g. commonjs, esm, react, typescript, risky, generated, ignored)
76 #[arg(long)]
77 tag: Option<String>,
78 },
79 /// Explain migration detection for a file
80 Explain { file: PathBuf },
81 /// Run a recipe against a target path
82 Run {
83 #[arg(required = true, num_args = 2.., value_name = "RECIPE... PATH")]
84 args: Vec<String>,
85 #[arg(long)]
86 write: bool,
87 #[arg(long)]
88 dry_run: bool,
89 #[arg(long)]
90 review: bool,
91 #[arg(long)]
92 autofix: bool,
93 #[arg(long)]
94 verbose: bool,
95 #[arg(long)]
96 summary_only: bool,
97 #[arg(long)]
98 max_preview_lines: Option<usize>,
99 #[arg(long)]
100 allow_risky: bool,
101 #[arg(long)]
102 strict: bool,
103 /// Generate JSON report
104 #[arg(long)]
105 report_json: bool,
106 /// Generate Markdown report
107 #[arg(long)]
108 report_md: bool,
109 /// Output directory for reports
110 #[arg(long, default_value = ".morph-cli/reports")]
111 report_dir: PathBuf,
112 /// Enable formatting preservation
113 #[arg(long)]
114 format: bool,
115 /// Use Prettier for formatting
116 #[arg(long)]
117 prettier: bool,
118 /// Disable formatting
119 #[arg(long)]
120 no_format: bool,
121 /// Number of parallel jobs (default: CPU count)
122 #[arg(long)]
123 jobs: Option<usize>,
124 /// Run sequentially (disable parallelism)
125 #[arg(long)]
126 sequential: bool,
127 /// Limit execution to a workspace package by package name
128 #[arg(long)]
129 package: Option<String>,
130 /// Profile name to load config overrides from
131 #[arg(long)]
132 profile: Option<String>,
133 /// Output style (minimal, default, detailed)
134 #[arg(long)]
135 output_style: Option<String>,
136 /// Filter by file tag (e.g. commonjs, esm, react, typescript, risky, generated, ignored)
137 #[arg(long)]
138 tag: Option<String>,
139 },
140 /// List migration run sessions
141 Sessions,
142 /// Show a migration run session
143 Session { id: String },
144 /// Rollback files from a previous transformation session
145 Rollback {
146 session_id: String,
147 #[arg(long)]
148 preview: bool,
149 #[arg(long)]
150 force: bool,
151 },
152 /// Replay a previous transformation session with stored options
153 Replay {
154 session_id: String,
155 /// Apply changes (write to disk), overrides stored mode if true
156 #[arg(long)]
157 write: bool,
158 },
159 /// Resume migration from a saved checkpoint
160 Resume {
161 /// Checkpoint ID to resume from
162 #[arg(long)]
163 checkpoint: String,
164 },
165 /// List scan snapshots
166 Scans,
167 /// Generate shell completion scripts
168 Completions {
169 /// The shell to generate completions for
170 #[arg(value_enum)]
171 shell: clap_complete::Shell,
172 },
173 /// Scan project and detect technologies
174 #[command(visible_alias = "s")]
175 Scan {
176 #[command(subcommand)]
177 action: Option<ScanAction>,
178
179 #[arg(default_value = ".")]
180 path: PathBuf,
181 /// Filter by file tag (e.g. commonjs, esm, react, typescript, risky, generated, ignored)
182 #[arg(long)]
183 tag: Option<String>,
184 /// Show verbose skip output
185 #[arg(short = 'v', long)]
186 verbose: bool,
187 },
188 /// Analyze lightweight dependencies
189 Deps {
190 #[arg(default_value = ".")]
191 path: PathBuf,
192 },
193 /// Calculate modernization score and readiness
194 Score {
195 #[arg(default_value = ".")]
196 path: PathBuf,
197 /// Output format (text or json)
198 #[arg(long, default_value = "text")]
199 format: String,
200 },
201 /// Simulate migration impact without transforming files
202 #[command(visible_alias = "sim")]
203 Simulate {
204 /// Recipes to simulate; repeat to select multiple recipes
205 #[arg(short = 'r', long = "recipe", required = true)]
206 recipes: Vec<String>,
207
208 #[arg(default_value = ".")]
209 path: PathBuf,
210 },
211 /// Start the local dashboard backend
212 Dashboard {
213 /// Port to listen on (default: 8080)
214 #[arg(long, default_value_t = 8080)]
215 port: u16,
216 },
217 /// Start a guided migration workflow
218 Magic {
219 #[arg(default_value = ".")]
220 path: PathBuf,
221 },
222 /// Run a full validation suite on a repository (scan, detect, dry-run, verify)
223 #[command(visible_alias = "validate")]
224 ValidateRepo {
225 #[arg(default_value = ".")]
226 path: PathBuf,
227 },
228 /// Benchmark Morph execution performance on the target path
229 #[command(visible_alias = "bench")]
230 Benchmark {
231 #[arg(default_value = ".")]
232 path: PathBuf,
233 },
234 /// Generate visualization graphs (Mermaid or JSON)
235 Graph {
236 /// Type of graph to generate (pipeline, workspace, deps)
237 #[arg(long, default_value = "pipeline")]
238 graph_type: String,
239
240 /// Output format (mermaid or json)
241 #[arg(long, default_value = "mermaid")]
242 format: String,
243
244 #[arg(default_value = ".")]
245 path: PathBuf,
246 },
247 /// Manage preset workflows
248 Preset {
249 #[command(subcommand)]
250 action: PresetAction,
251 },
252 /// Verify the integrity of transformed files (syntax, imports, exports)
253 Verify {
254 #[arg(default_value = ".")]
255 path: PathBuf,
256 },
257 /// Watch for file changes and re-run migration analysis
258 Watch {
259 #[arg(default_value = ".")]
260 path: PathBuf,
261 /// Recipe to analyze; repeat to select multiple recipes
262 #[arg(long = "recipe")]
263 recipes: Vec<String>,
264 /// Debounce delay in milliseconds
265 #[arg(long, default_value_t = 500)]
266 debounce_ms: u64,
267 },
268 /// AI-powered suggestions and analysis
269 Ai {
270 #[command(subcommand)]
271 action: AiAction,
272 },
273 /// Manage and run migration manifests
274 Manifest {
275 #[command(subcommand)]
276 action: ManifestAction,
277 },
278 /// Manage plugins
279 Plugins {
280 #[command(subcommand)]
281 action: PluginAction,
282 },
283 /// View ignored and skipped files with reasons
284 Ignored {
285 #[arg(default_value = ".")]
286 path: PathBuf,
287 /// Show detailed list of all ignored files instead of a summary
288 #[arg(short, long)]
289 detailed: bool,
290 },
291 /// List all dry-run execution snapshots
292 DryRuns,
293 /// View detailed metrics of a specific dry-run execution snapshot
294 DryRun {
295 #[command(subcommand)]
296 action: DryRunAction,
297 },
298 /// Generate markdown documentation for Morph CLI components
299 Docs,
300 /// Check repository health and environment
301 Doctor {
302 #[arg(default_value = ".")]
303 path: PathBuf,
304 },
305}
306
307#[derive(Debug, Subcommand)]
308pub enum AiAction {
309 /// Suggest improvements for a specific file
310 Suggest { file: PathBuf },
311}
312
313#[derive(Debug, Subcommand)]
314pub enum PresetAction {
315 /// List all built-in presets
316 List,
317 /// Run a specific preset workflow
318 Run {
319 /// Name of the preset to run
320 name: String,
321 /// Target path (defaults to current directory)
322 #[arg(default_value = ".")]
323 path: PathBuf,
324 /// Apply changes (write to disk)
325 #[arg(long)]
326 write: bool,
327 },
328}
329
330#[derive(Debug, Subcommand)]
331pub enum PluginAction {
332 /// List all discovered plugins
333 List,
334 /// Show detailed info about a plugin
335 Info { name: String },
336}
337
338#[derive(Debug, Subcommand, Clone)]
339pub enum ManifestAction {
340 /// Create a migration manifest file
341 Create {
342 /// Path to save the manifest (e.g. morph-migration.toml)
343 #[arg(default_value = "morph-migration.toml")]
344 file: std::path::PathBuf,
345 /// Optional configuration profile to use
346 #[arg(long)]
347 profile: Option<String>,
348 /// Recipes to execute; repeat to specify multiple recipes
349 #[arg(short = 'r', long = "recipe")]
350 recipes: Vec<String>,
351 /// Target path to apply migration to (defaults to current directory)
352 #[arg(short = 't', long = "target", default_value = ".")]
353 target: std::path::PathBuf,
354 /// Apply changes (write to disk)
355 #[arg(long)]
356 write: bool,
357 /// Simulate execution without writing to disk
358 #[arg(long)]
359 dry_run: bool,
360 /// Allow recipes to execute even with potential risks
361 #[arg(long)]
362 allow_risky: bool,
363 /// Enforce strict type checking and zero warnings
364 #[arg(long)]
365 strict: bool,
366 /// Automatically fix minor lint/style issues
367 #[arg(long)]
368 autofix: bool,
369 },
370 /// Run a migration manifest file
371 Run {
372 /// Path to the manifest file (e.g. morph-migration.toml)
373 file: std::path::PathBuf,
374 },
375}
376
377#[derive(Debug, Subcommand, Clone)]
378pub enum DryRunAction {
379 /// Show a specific dry-run execution snapshot by ID
380 Show { id: String },
381}
382
383#[derive(Debug, Subcommand, Clone)]
384pub enum ScanAction {
385 /// Show a specific scan snapshot by ID
386 Show { id: String },
387}
388
389pub fn parse() -> Cli {
390 Cli::parse()
391}