1use anyhow::Result;
4
5struct Entry {
7 command: &'static str,
8 aliases: &'static str,
9 group: &'static str,
10 description: &'static str,
11}
12
13const CATALOG: &[Entry] = &[
14 Entry {
15 command: "create",
16 aliases: "new",
17 group: "Global",
18 description: "Write .run/run.config.toml and run init",
19 },
20 Entry {
21 command: "migrate",
22 aliases: "—",
23 group: "Global",
24 description: "Convert legacy run/ to .run/ (config + env)",
25 },
26 Entry {
27 command: "upgrade",
28 aliases: "update",
29 group: "Global",
30 description: "Refresh legacy run/ or self-update",
31 },
32 Entry {
33 command: "self-update",
34 aliases: "—",
35 group: "Global",
36 description: "Install latest from crates.io and migrate configs",
37 },
38 Entry {
39 command: "completion",
40 aliases: "—",
41 group: "Global",
42 description: "Shell / Kiro autocompletion (bash|zsh|fig|install|services)",
43 },
44 Entry {
45 command: "init",
46 aliases: "—",
47 group: "Setup",
48 description: "Write .run/run.config.toml and .run/.env",
49 },
50 Entry {
51 command: "init --update",
52 aliases: "—",
53 group: "Setup",
54 description: "Find and add apps added to the workspace since setup",
55 },
56 Entry {
57 command: "up",
58 aliases: "run",
59 group: "Lifecycle",
60 description: "Build if needed and start (--essential = config essentials)",
61 },
62 Entry {
63 command: "down",
64 aliases: "—",
65 group: "Lifecycle",
66 description: "Stop everything, or named services (keeps data)",
67 },
68 Entry {
69 command: "restart",
70 aliases: "—",
71 group: "Lifecycle",
72 description: "Stop then start (down + up); --build, --essential",
73 },
74 Entry {
75 command: "rebuild",
76 aliases: "—",
77 group: "Lifecycle",
78 description: "Rebuild images from scratch and recreate containers",
79 },
80 Entry {
81 command: "clean",
82 aliases: "—",
83 group: "Lifecycle",
84 description: "Stop and DELETE all volumes",
85 },
86 Entry {
87 command: "commands",
88 aliases: "—",
89 group: "Inspection",
90 description: "List every CLI command in a table",
91 },
92 Entry {
93 command: "apps",
94 aliases: "list",
95 group: "Inspection",
96 description: "List configured apps (backend, web, mobile, …)",
97 },
98 Entry {
99 command: "services",
100 aliases: "—",
101 group: "Inspection",
102 description: "List compose services (and status when Docker is up)",
103 },
104 Entry {
105 command: "ps",
106 aliases: "status",
107 group: "Inspection",
108 description: "Service status",
109 },
110 Entry {
111 command: "ports",
112 aliases: "—",
113 group: "Inspection",
114 description: "Host ports + env URLs table",
115 },
116 Entry {
117 command: "logs",
118 aliases: "—",
119 group: "Inspection",
120 description: "Follow logs",
121 },
122 Entry {
123 command: "shell",
124 aliases: "sh",
125 group: "Inspection",
126 description: "Shell into a container (default: backend)",
127 },
128 Entry {
129 command: "config",
130 aliases: "—",
131 group: "Inspection",
132 description: "Print the resolved configuration",
133 },
134 Entry {
135 command: "explain",
136 aliases: "—",
137 group: "Inspection",
138 description: "Print the docker compose command, run nothing",
139 },
140 Entry {
141 command: "env",
142 aliases: "—",
143 group: "Inspection",
144 description: "Print the environment compose is given",
145 },
146 Entry {
147 command: "generate",
148 aliases: "—",
149 group: "Inspection",
150 description: "Write the compose overlays, start nothing",
151 },
152 Entry {
153 command: "backend",
154 aliases: "—",
155 group: "Backend",
156 description: "Run any command inside the backend container",
157 },
158 Entry {
159 command: "migrate",
160 aliases: "—",
161 group: "Backend",
162 description: "Run pending migrations",
163 },
164 Entry {
165 command: "seed",
166 aliases: "—",
167 group: "Backend",
168 description: "Run database seeders",
169 },
170 Entry {
171 command: "fresh",
172 aliases: "—",
173 group: "Backend",
174 description: "Rebuild the schema and seed it (destroys data)",
175 },
176 Entry {
177 command: "artisan",
178 aliases: "—",
179 group: "Backend",
180 description: "php artisan … (BACKEND_STACK=laravel)",
181 },
182 Entry {
183 command: "composer",
184 aliases: "—",
185 group: "Backend",
186 description: "composer … (BACKEND_STACK=laravel)",
187 },
188 Entry {
189 command: "pnpm",
190 aliases: "—",
191 group: "Frontend",
192 description: "pnpm … inside the frontend workspace",
193 },
194 Entry {
195 command: "ios",
196 aliases: "—",
197 group: "Mobile",
198 description: "Open mobile-client in the iOS Simulator",
199 },
200 Entry {
201 command: "android",
202 aliases: "—",
203 group: "Mobile",
204 description: "Open mobile-client on an Android emulator",
205 },
206 Entry {
207 command: "device",
208 aliases: "—",
209 group: "Mobile",
210 description: "Open on a USB phone, or print the Expo Go URL",
211 },
212 Entry {
213 command: "mobile",
214 aliases: "—",
215 group: "Mobile",
216 description: "Restart mobile-deps / mobile-packages / Metro",
217 },
218 Entry {
219 command: "reload",
220 aliases: "—",
221 group: "Mobile",
222 description: "Metro /status + /reload; --clear wipes caches",
223 },
224 Entry {
225 command: "prebuild",
226 aliases: "—",
227 group: "Mobile",
228 description: "expo prebuild (Expo apps only)",
229 },
230 Entry {
231 command: "sync-mobile-port",
232 aliases: "—",
233 group: "Mobile",
234 description: "Sync MOBILE_CLIENT_PORT into the mobile app configs",
235 },
236 Entry {
237 command: "bundler",
238 aliases: "configure-bundler",
239 group: "Mobile",
240 description: "Point the simulator at Metro",
241 },
242 Entry {
243 command: "desktop",
244 aliases: "—",
245 group: "Desktop",
246 description: "Launch the Electron / Tauri shell on the host",
247 },
248 Entry {
249 command: "deploy",
250 aliases: "—",
251 group: "Deploy",
252 description: "Deploy web, mobile or desktop",
253 },
254 Entry {
255 command: "dash",
256 aliases: "dashboard",
257 group: "Dashboard",
258 description: "Open the status dashboard in a browser",
259 },
260];
261
262pub fn run(raw: bool) -> Result<i32> {
263 if raw {
264 let mut seen = std::collections::BTreeSet::new();
265 for entry in CATALOG {
266 if seen.insert(entry.command) {
267 println!("{}", entry.command);
268 }
269 }
270 return Ok(0);
271 }
272 print_table();
273 Ok(0)
274}
275
276fn print_table() {
277 let cmd_w = width(CATALOG.iter().map(|e| e.command), "COMMAND");
278 let alias_w = width(CATALOG.iter().map(|e| e.aliases), "ALIASES");
279 let group_w = width(CATALOG.iter().map(|e| e.group), "GROUP");
280
281 println!("Commands");
282 println!(
283 "{:<cmd_w$} {:<alias_w$} {:<group_w$} {}",
284 "COMMAND", "ALIASES", "GROUP", "DESCRIPTION"
285 );
286 let rule = cmd_w + alias_w + group_w + 6 + 40;
287 println!("{}", "-".repeat(rule.max(24)));
288 for entry in CATALOG {
289 println!(
290 "{:<cmd_w$} {:<alias_w$} {:<group_w$} {}",
291 entry.command, entry.aliases, entry.group, entry.description
292 );
293 }
294 println!();
295}
296
297fn width<'a>(values: impl Iterator<Item = &'a str>, header: &str) -> usize {
298 values
299 .map(str::len)
300 .chain(std::iter::once(header.len()))
301 .max()
302 .unwrap_or(header.len())
303}