1mod ai;
2mod benchmark;
3mod dashboard;
4mod deps;
5mod doctor;
6mod docs;
7mod explain;
8mod graph;
9mod history;
10mod init;
11mod list;
12mod magic;
13mod plan;
14mod plugins;
15mod preset;
16mod run;
17mod scan;
18mod score;
19mod simulate;
20mod search;
21mod sessions;
22mod validate;
23mod verify;
24mod version;
25mod watch;
26mod completions;
27mod manifest;
28mod ignored;
29mod dry_run;
30
31use std::path::Path;
32
33use anyhow::{Result, bail};
34
35use crate::cli::{Cli, Command, PluginAction, PresetAction, AiAction, ScanAction};
36use crate::commands::sessions::SessionsArgs;
37
38pub fn execute(cli: Cli, project_root: &Path) -> Result<()> {
39 match cli.command {
40 Command::Version => version::execute(),
41 Command::List { path, category, tag } => list::execute(&path, category.as_deref(), tag.as_deref()),
42 Command::Search { query, path, category, maturity } => {
43 search::execute(&query, &path, category.as_deref(), maturity.as_deref())
44 }
45 Command::History { limit } => history::execute(limit, project_root),
46 Command::Init { path } => init::execute(&path),
47 Command::Dashboard { port } => dashboard::execute(port, project_root),
48 Command::Magic { path } => magic::execute(&path, project_root),
49 Command::Benchmark { path } => benchmark::execute(&path, project_root),
50 Command::ValidateRepo { path } => validate::execute(&path, project_root),
51 Command::Ai { action } => match action {
52 AiAction::Suggest { file } => ai::suggest(&file),
53 },
54 Command::Sessions => sessions::execute(project_root, &SessionsArgs::List),
55 Command::Session { id } => sessions::execute(project_root, &SessionsArgs::Show { id }),
56 Command::Plan { path, tag } => plan::execute(&path, tag.as_deref()),
57 Command::Explain { file } => explain::execute(&file),
58 Command::Scans => scan::execute_list(project_root),
59 Command::Scan { action, path, tag, verbose } => {
60 if let Some(act) = action {
61 match act {
62 ScanAction::Show { id } => scan::execute_show(&id, project_root),
63 }
64 } else {
65 scan::execute(&path, tag.as_deref(), verbose, project_root)
66 }
67 }
68 Command::Deps { path } => deps::execute(&path),
69 Command::Score { path, format } => score::execute(&path, &format),
70 Command::Simulate { recipes, path } => simulate::execute(&recipes, &path),
71 Command::Graph { graph_type, format, path } => graph::execute(&graph_type, &format, &path),
72 Command::Preset { action } => match action {
73 PresetAction::List => preset::list(),
74 PresetAction::Run { name, path, write } => preset::run(&name, &path, write, project_root),
75 },
76 Command::Watch {
77 path,
78 recipes,
79 debounce_ms,
80 } => watch::execute(&path, &recipes, debounce_ms),
81 Command::Rollback {
82 session_id,
83 preview,
84 force,
85 } => sessions::execute(
86 project_root,
87 &SessionsArgs::Rollback {
88 session_id,
89 preview,
90 force,
91 },
92 ),
93 Command::Verify { path } => verify::execute(&path),
94 Command::Replay { session_id, write } => sessions::execute(
95 project_root,
96 &SessionsArgs::Replay {
97 session_id,
98 write,
99 },
100 ),
101 Command::Resume { checkpoint } => run::resume(&checkpoint, project_root),
102 Command::Completions { shell } => completions::execute(shell),
103 Command::Manifest { action } => manifest::execute(action, project_root),
104 Command::Ignored { path, detailed } => ignored::execute(&path, detailed),
105 Command::DryRuns => dry_run::execute_list(project_root),
106 Command::DryRun { action } => dry_run::execute_show(action, project_root),
107 Command::Run {
108 args,
109 write,
110 dry_run,
111 review,
112 verbose,
113 summary_only,
114 max_preview_lines,
115 allow_risky,
116 strict,
117 report_json,
118 report_md,
119 report_dir,
120 format,
121 prettier,
122 no_format,
123 jobs,
124 sequential,
125 autofix,
126 package,
127 profile,
128 output_style,
129 tag,
130 } => {
131 let (path, recipes) = split_run_args(args)?;
132 run::execute(
133 &recipes,
134 &path,
135 dry_run,
136 write,
137 review,
138 autofix,
139 verbose,
140 summary_only,
141 max_preview_lines,
142 allow_risky,
143 strict,
144 report_json,
145 report_md,
146 &report_dir,
147 format,
148 prettier,
149 no_format,
150 jobs,
151 sequential,
152 project_root,
153 package.as_deref(),
154 profile.as_deref(),
155 output_style.as_deref(),
156 tag.as_deref(),
157 )
158 }
159 Command::Plugins { action } => match action {
160 PluginAction::List => plugins::list::execute(project_root),
161 PluginAction::Info { name } => plugins::info::execute(&name),
162 },
163 Command::Docs => docs::execute(),
164 Command::Doctor { path } => doctor::execute(&path),
165 }
166}
167
168fn split_run_args(mut args: Vec<String>) -> Result<(std::path::PathBuf, Vec<String>)> {
169 let Some(path) = args.pop() else {
170 bail!("Usage: morph run <recipe...> <path>");
171 };
172
173 if args.is_empty() {
174 bail!("Usage: morph run <recipe...> <path>");
175 }
176
177 Ok((std::path::PathBuf::from(path), args))
178}