numux 2.10.4 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1127 @@
1
+ .TH "NUMUX" "1" "April 2026" "2.11.0" "numux manual"
2
+ .SH "NAME"
3
+ \fBnumux\fR
4
+ .P
5
+ Terminal multiplexer with dependency orchestration\. Run multiple processes in a tabbed TUI with a dependency graph controlling startup order, readiness detection, and output capture between processes\.
6
+ .P
7
+ Works with zero configuration — pass commands as arguments, run a script across monorepo workspaces with \fB\-w\fP, or match multiple scripts with glob patterns like \fB'dev:*'\fP\|\. For advanced setups, define a typed config with conditional processes, file watching with auto\-restart, error detection, log persistence, and output capture — e\.g\. extract a port from one process's stdout and pass it to another process's command or env\.
8
+ .P
9
+ Inspired by \fBsst dev\fP and \fBconcurrently\fP
10
+ .SH Install
11
+ .P
12
+ Requires
13
+ .UR https://bun.sh
14
+ .I Bun
15
+ .UE
16
+ >= 1\.0\.
17
+ .RS 2
18
+ .nf
19
+ bun install \-g numux
20
+ .fi
21
+ .RE
22
+ .SH Usage
23
+ .SS Quick start
24
+ .RS 2
25
+ .nf
26
+ numux init
27
+ .fi
28
+ .RE
29
+ .P
30
+ This creates a starter \fBnumux\.config\.ts\fP with commented\-out examples\. Edit it, then run \fBnumux\fP\|\.
31
+ .SS Config file
32
+ .P
33
+ Create \fBnumux\.config\.ts\fP (or \fB\|\.js\fP):
34
+ .RS 2
35
+ .nf
36
+ import { defineConfig } from 'numux'
37
+
38
+ export default defineConfig({
39
+ processes: {
40
+ db: {
41
+ command: 'docker compose up postgres',
42
+ readyPattern: 'ready to accept connections',
43
+ },
44
+ migrate: {
45
+ command: 'bun run migrate',
46
+ dependsOn: ['db'],
47
+ },
48
+ api: {
49
+ command: 'bun run dev:api',
50
+ dependsOn: ['migrate'],
51
+ readyPattern: 'listening on port 3000',
52
+ },
53
+ // String shorthand for simple processes
54
+ web: 'bun run dev:web',
55
+ // Interactive process — keyboard input is forwarded
56
+ confirm: {
57
+ command: 'sh \-c "printf \\'Deploy to staging? [y/n] \\' && read answer && echo $answer"',
58
+ interactive: true,
59
+ },
60
+ },
61
+ })
62
+ .fi
63
+ .RE
64
+ .P
65
+ The \fBdefineConfig()\fP helper is optional — it provides type checking for your config\.
66
+ .P
67
+ Processes can be a string (shorthand for \fB{ command: "\.\.\." }\fP), \fBtrue\fP or \fB{}\fP (auto\-resolves to a matching \fBpackage\.json\fP script), or a full config object\.
68
+ .P
69
+ Then run:
70
+ .RS 2
71
+ .nf
72
+ numux
73
+ .fi
74
+ .RE
75
+ .SS Subcommands
76
+ <!\-\- generated:subcommands \-\->
77
+ .RS 2
78
+ .nf
79
+ numux init # Create a starter config file
80
+ numux validate # Validate config and show process graph
81
+ numux exec <name> [\-\-] <cmd> # Run a command in a process's environment
82
+ numux logs [name] # Open the log directory or a specific process log
83
+ numux completions <shell> # Generate shell completions (bash, zsh, fish)
84
+ numux help [topic] # Show help for a topic
85
+ .fi
86
+ .RE
87
+ <!\-\- /generated:subcommands \-\->
88
+
89
+ .P
90
+ \fBvalidate\fP respects \fB\-\-only\fP/\fB\-\-exclude\fP filters and shows processes grouped by dependency tiers\.
91
+ .P
92
+ \fBexec\fP runs a one\-off command using a process's configured \fBcwd\fP, \fBenv\fP, and \fBenvFile\fP — useful for migrations, scripts, or any command that needs the same environment:
93
+ .RS 2
94
+ .nf
95
+ numux exec api \-\- npx prisma migrate
96
+ numux exec web npm run build
97
+ .fi
98
+ .RE
99
+ .P
100
+ \fBlogs\fP prints the log directory path, or a specific process's log contents:
101
+ .RS 2
102
+ .nf
103
+ numux logs # Print log directory path
104
+ numux logs api # Print the api process log
105
+ .fi
106
+ .RE
107
+ .P
108
+ Set up completions for your shell:
109
+ .RS 2
110
+ .nf
111
+ # Bash (add to ~/\.bashrc)
112
+ eval "$(numux completions bash)"
113
+
114
+ # Zsh (add to ~/\.zshrc)
115
+ eval "$(numux completions zsh)"
116
+
117
+ # Fish
118
+ numux completions fish | source
119
+ # Or save permanently:
120
+ numux completions fish > ~/\.config/fish/completions/numux\.fish
121
+ .fi
122
+ .RE
123
+ .SS Workspaces
124
+ .P
125
+ Run a \fBpackage\.json\fP script across all workspaces in a monorepo:
126
+ .RS 2
127
+ .nf
128
+ numux \-w dev
129
+ .fi
130
+ .RE
131
+ .P
132
+ Reads the \fBworkspaces\fP field from your root \fBpackage\.json\fP, finds which workspaces have the given script, and spawns \fB<pm> run <script>\fP in each\. The package manager is auto\-detected from \fBpackageManager\fP field or lockfiles\.
133
+ .P
134
+ Composes with other flags:
135
+ .RS 2
136
+ .nf
137
+ numux \-w dev \-n redis="redis\-server" \-\-colors
138
+ .fi
139
+ .RE
140
+ .SS Ad\-hoc commands
141
+ .RS 2
142
+ .nf
143
+ # Unnamed (name derived from command)
144
+ numux "bun dev:api" "bun dev:web"
145
+
146
+ # Named process
147
+ numux \-n api="bun dev:api" \-n web="bun dev:web"
148
+ .fi
149
+ .RE
150
+ .SS Script patterns
151
+ .P
152
+ Run package\.json scripts by name — any colon\-containing name is automatically recognized as a script reference:
153
+ .RS 2
154
+ .nf
155
+ numux 'lint:eslint \-\-fix' # runs: yarn run lint:eslint \-\-fix
156
+ numux 'dev:*' # all scripts matching dev:*
157
+ numux 'npm:*:dev' # explicit npm: prefix (same behavior)
158
+ .fi
159
+ .RE
160
+ <!\-\- generated:script\-pattern\-rules \-\->
161
+ .SH Script pattern rules
162
+ .P
163
+ \fBRecognition:\fR A process name is treated as a script reference when it:
164
+
165
+ .RS 1
166
+ .IP \(bu 2
167
+ starts with \fBnpm:\fP (e\.g\. \fBnpm:dev:*\fP)
168
+ .IP \(bu 2
169
+ contains glob metacharacters (\fB*\fP, \fB?\fP, \fB[\fP)
170
+ .IP \(bu 2
171
+ contains a colon AND has no explicit \fBcommand\fP (e\.g\. \fBlint:eslint: {}\fP)
172
+
173
+ .RE
174
+ .P
175
+ \fBGlob matching:\fR Patterns are matched against \fBpackage\.json\fP scripts using
176
+ .br
177
+ \fBBun\.Glob\fP\|\. The \fB*\fP wildcard does NOT match across \fB:\fP separators — \fBdev:*\fP
178
+ .br
179
+ matches \fBdev:web\fP but not \fBdev:web:hmr\fP\|\. Use \fBdev:*:*\fP for two levels deep\.
180
+ .P
181
+ \fBLeaf\-only (\fB^\fP):\fR Append \fB^\fP to skip scripts that are group runners —
182
+ .br
183
+ scripts that have sub\-scripts beneath them\. E\.g\. if \fBformat:check\fP has
184
+ .br
185
+ \fBformat:check:store\fP and \fBformat:check:odoo\fP below it, \fBformat:*^\fP excludes
186
+ .br
187
+ \fBformat:check\fP but keeps the leaf scripts\.
188
+ .P
189
+ \fBExtra args:\fR Anything after the first space in the pattern is forwarded
190
+ .br
191
+ as extra arguments to each matched command: \fBlint:* \-\-fix\fP → \fBbun run lint:js \-\- \-\-fix\fP\|\.
192
+ .P
193
+ \fBTemplate inheritance:\fR Config properties on a pattern entry (color, env,
194
+ .br
195
+ dependsOn, etc\.) are inherited by all expanded processes\. Color arrays are
196
+ .br
197
+ distributed round\-robin across matches\.
198
+ .P
199
+ \fBDisplay names:\fR The glob's literal prefix and suffix are stripped from
200
+ .br
201
+ matched script names: \fBdev:*\fP + \fBdev:web\fP → display name \fBweb\fP\|\.
202
+ .SH Auto\-resolution
203
+ .P
204
+ When a process has no \fBcommand\fP and its name matches a \fBpackage\.json\fP script,
205
+ .br
206
+ the command is auto\-resolved to \fB<pm> run <name>\fP\|\. This works for:
207
+
208
+ .RS 1
209
+ .IP \(bu 2
210
+ \fBtrue\fP or \fB{}\fP shorthand: \fBlint: true\fP → \fBbun run lint\fP
211
+ .IP \(bu 2
212
+ Objects without \fBcommand\fP: \fBtypecheck: { dependsOn: [&#39;db&#39;] }\fP → \fBbun run typecheck\fP
213
+
214
+ .RE
215
+ .SH npm: prefix
216
+ .P
217
+ Commands starting with \fBnpm:\fP are rewritten to use the detected package
218
+ .br
219
+ manager: \fBnpm:dev\fP → \fBbun run dev\fP (if bun is detected)\.
220
+ <!\-\- /generated:script\-pattern\-rules \-\->
221
+
222
+ .RS 2
223
+ .nf
224
+ numux 'lint:* \-\-fix' # → bun run lint:js \-\-fix, bun run lint:ts \-\-fix
225
+ .fi
226
+ .RE
227
+ .P
228
+ In a config file, use the pattern as the process name:
229
+ .RS 2
230
+ .nf
231
+ export default defineConfig({
232
+ processes: {
233
+ 'dev:*': { color: ['green', 'cyan'] },
234
+ 'lint:* \-\-fix': {},
235
+ },
236
+ })
237
+ .fi
238
+ .RE
239
+ .P
240
+ Auto\-resolution example:
241
+ .RS 2
242
+ .nf
243
+ export default defineConfig({
244
+ processes: {
245
+ lint: true, // → bun run lint
246
+ typecheck: { dependsOn: ['db'] }, // → bun run typecheck (with dependency)
247
+ db: 'docker compose up postgres', // explicit command, not resolved
248
+ },
249
+ })
250
+ .fi
251
+ .RE
252
+ .SS Options
253
+ <!\-\- generated:options \-\->
254
+ .TS
255
+ tab(|) expand nowarn box;
256
+ l l.
257
+ T{
258
+ Flag
259
+ T}|T{
260
+ Description
261
+ T}
262
+ =
263
+ T{
264
+ \fB\-s,\fP \fB\-\-sort\fP `<config
265
+ T}|T{
266
+ alphabetical
267
+ T}
268
+ _
269
+ T{
270
+ \fB\-w,\fP \fB\-\-workspace\fP \fB<script>\fP
271
+ T}|T{
272
+ Run a package\.json script across all workspaces
273
+ T}
274
+ _
275
+ T{
276
+ \fB\-n,\fP \fB\-\-name\fP \fB<name=command>\fP
277
+ T}|T{
278
+ Add a named process
279
+ T}
280
+ _
281
+ T{
282
+ \fB\-c,\fP \fB\-\-color\fP \fB<colors>\fP
283
+ T}|T{
284
+ Comma\-separated colors (hex or names: black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple)
285
+ T}
286
+ _
287
+ T{
288
+ \fB\-\-colors\fP
289
+ T}|T{
290
+ Auto\-assign colors to processes based on their name
291
+ T}
292
+ _
293
+ T{
294
+ \fB\-e,\fP \fB\-\-env\-file\fP `<path
295
+ T}|T{
296
+ false>`
297
+ T}
298
+ _
299
+ T{
300
+ \fB\-\-config\fP \fB<path>\fP
301
+ T}|T{
302
+ Config file path (default: auto\-detect)
303
+ T}
304
+ _
305
+ T{
306
+ \fB\-p,\fP \fB\-\-prefix\fP
307
+ T}|T{
308
+ Prefixed output mode (no TUI, for CI/scripts)
309
+ T}
310
+ _
311
+ T{
312
+ \fB\-\-only\fP \fB<a,b,\.\.\.>\fP
313
+ T}|T{
314
+ Only run these processes (+ their dependencies)
315
+ T}
316
+ _
317
+ T{
318
+ \fB\-\-exclude\fP \fB<a,b,\.\.\.>\fP
319
+ T}|T{
320
+ Exclude these processes
321
+ T}
322
+ _
323
+ T{
324
+ \fB\-\-kill\-others\fP
325
+ T}|T{
326
+ Kill all processes when any exits (regardless of exit code)
327
+ T}
328
+ _
329
+ T{
330
+ \fB\-\-kill\-others\-on\-fail\fP
331
+ T}|T{
332
+ Kill all processes when any exits with non\-zero code
333
+ T}
334
+ _
335
+ T{
336
+ \fB\-\-max\-restarts\fP \fB<n>\fP
337
+ T}|T{
338
+ Max auto\-restarts for crashed processes
339
+ T}
340
+ _
341
+ T{
342
+ \fB\-\-no\-watch\fP
343
+ T}|T{
344
+ Disable file watching even if config has watch patterns
345
+ T}
346
+ _
347
+ T{
348
+ \fB\-t,\fP \fB\-\-timestamps\fP \fB[<format>]\fP
349
+ T}|T{
350
+ Add timestamps to output (default HH:mm:ss, or pass a format string)
351
+ T}
352
+ _
353
+ T{
354
+ \fB\-\-log\-dir\fP \fB<path>\fP
355
+ T}|T{
356
+ Write per\-process logs to directory
357
+ T}
358
+ _
359
+ T{
360
+ \fB\-\-debug\fP
361
+ T}|T{
362
+ Enable debug logging to \.numux/debug\.log
363
+ T}
364
+ _
365
+ T{
366
+ \fB\-h,\fP \fB\-\-help\fP
367
+ T}|T{
368
+ Show this help
369
+ T}
370
+ _
371
+ T{
372
+ \fB\-v,\fP \fB\-\-version\fP
373
+ T}|T{
374
+ Show version
375
+ T}
376
+ .TE
377
+ <!\-\- /generated:options \-\->
378
+
379
+ .SS Prefix mode
380
+ .P
381
+ Use \fB\-\-prefix\fP (\fB\-p\fP) for CI or headless environments\. Output is printed with colored \fB[name]\fP prefixes instead of the TUI:
382
+ .RS 2
383
+ .nf
384
+ numux \-\-prefix
385
+ .fi
386
+ .RE
387
+ .P
388
+ Auto\-exits when all processes finish\. Exit code 1 if any process failed\.
389
+ .SH Config reference
390
+ .SS Global options
391
+ .P
392
+ Top\-level options apply to all processes (process\-level settings override):
393
+ <!\-\- generated:config\-global \-\->
394
+ .TS
395
+ tab(|) expand nowarn box;
396
+ l l l.
397
+ T{
398
+ Field
399
+ T}|T{
400
+ Type
401
+ T}|T{
402
+ Description
403
+ T}
404
+ =
405
+ T{
406
+ \fBcwd\fP
407
+ T}|T{
408
+ \fBstring\fP
409
+ T}|T{
410
+ Global working directory, inherited by all processes
411
+ T}
412
+ _
413
+ T{
414
+ \fBenv\fP
415
+ T}|T{
416
+ \fBRecord<string, string>\fP
417
+ T}|T{
418
+ Global env vars, merged into each process (process\-level overrides)
419
+ T}
420
+ _
421
+ T{
422
+ \fBenvFile\fP
423
+ T}|T{
424
+ \fBstring | string[] | false\fP
425
+ T}|T{
426
+ Global \.env file(s), inherited by processes without their own envFile; \fBfalse\fP disables
427
+ T}
428
+ _
429
+ T{
430
+ \fBshowCommand\fP
431
+ T}|T{
432
+ \fBboolean\fP
433
+ T}|T{
434
+ Global showCommand flag, inherited by all processes
435
+ T}
436
+ _
437
+ T{
438
+ \fBmaxRestarts\fP
439
+ T}|T{
440
+ \fBnumber\fP
441
+ T}|T{
442
+ Global restart limit, inherited by all processes (only restarts on non\-zero exit)
443
+ T}
444
+ _
445
+ T{
446
+ \fBreadyTimeout\fP
447
+ T}|T{
448
+ \fBnumber\fP
449
+ T}|T{
450
+ Global ready timeout (ms), inherited by all processes
451
+ T}
452
+ _
453
+ T{
454
+ \fBstopSignal\fP
455
+ T}|T{
456
+ \fB&#39;SIGTERM&#39; | &#39;SIGINT&#39; | &#39;SIGHUP&#39;\fP
457
+ T}|T{
458
+ Global stop signal, inherited by all processes
459
+ T}
460
+ _
461
+ T{
462
+ \fBerrorMatcher\fP
463
+ T}|T{
464
+ \fBboolean | string\fP
465
+ T}|T{
466
+ Global error matcher, inherited by all processes\. \fBtrue\fP = detect ANSI red output, string = regex
467
+ T}
468
+ _
469
+ T{
470
+ \fBwatch\fP
471
+ T}|T{
472
+ \fBstring | string[]\fP
473
+ T}|T{
474
+ Global watch patterns, inherited by processes without their own watch
475
+ T}
476
+ _
477
+ T{
478
+ \fBsort\fP
479
+ T}|T{
480
+ \fB&#39;config&#39; | &#39;alphabetical&#39; | &#39;topological&#39;\fP
481
+ T}|T{
482
+ Tab display order\. \fB&#39;config&#39;\fP preserves definition order (package\.json script order for wildcards), \fB&#39;alphabetical&#39;\fP sorts by process name, \fB&#39;topological&#39;\fP sorts by dependency tiers\.
483
+ T}
484
+ _
485
+ T{
486
+ \fBprefix\fP
487
+ T}|T{
488
+ \fBboolean\fP
489
+ T}|T{
490
+ Use prefixed output mode instead of TUI (for CI/scripts)
491
+ T}
492
+ _
493
+ T{
494
+ \fBtimestamps\fP
495
+ T}|T{
496
+ \fBboolean | string\fP
497
+ T}|T{
498
+ Add timestamps to output lines\. \fBtrue\fP uses default \fBHH:mm:ss\fP format, or pass a format string (e\.g\. \fB&quot;HH:mm:ss\.SSS&quot;\fP)
499
+ T}
500
+ _
501
+ T{
502
+ \fBkillOthers\fP
503
+ T}|T{
504
+ \fBboolean\fP
505
+ T}|T{
506
+ Kill all processes when any one exits (regardless of exit code)
507
+ T}
508
+ _
509
+ T{
510
+ \fBkillOthersOnFail\fP
511
+ T}|T{
512
+ \fBboolean\fP
513
+ T}|T{
514
+ Kill all processes when any one exits with a non\-zero exit code
515
+ T}
516
+ _
517
+ T{
518
+ \fBnoWatch\fP
519
+ T}|T{
520
+ \fBboolean\fP
521
+ T}|T{
522
+ Disable file watching even if processes have watch patterns
523
+ T}
524
+ _
525
+ T{
526
+ \fBlogDir\fP
527
+ T}|T{
528
+ \fBstring\fP
529
+ T}|T{
530
+ Directory to write per\-process log files
531
+ T}
532
+ .TE
533
+ <!\-\- /generated:config\-global \-\->
534
+
535
+ .RS 2
536
+ .nf
537
+ export default defineConfig({
538
+ cwd: '\./packages/backend',
539
+ env: { NODE_ENV: 'development' },
540
+ envFile: '\.env',
541
+ processes: {
542
+ api: { command: 'node server\.js' }, // inherits cwd, env, envFile
543
+ web: { command: 'vite', cwd: '\./packages/web' }, // overrides cwd
544
+ },
545
+ })
546
+ .fi
547
+ .RE
548
+ .SS Process options
549
+ .P
550
+ Each process accepts:
551
+ <!\-\- generated:config\-process \-\->
552
+ .TS
553
+ tab(|) expand nowarn box;
554
+ l l l l.
555
+ T{
556
+ Field
557
+ T}|T{
558
+ Type
559
+ T}|T{
560
+ Default
561
+ T}|T{
562
+ Description
563
+ T}
564
+ =
565
+ T{
566
+ \fBcommand\fP
567
+ T}|T{
568
+ \fBstring\fP
569
+ T}|T{
570
+ \fIrequired\fR
571
+ T}|T{
572
+ Shell command to run\. Supports \fB$dep\.group\fP references from dependency capture groups
573
+ T}
574
+ _
575
+ T{
576
+ \fBcwd\fP
577
+ T}|T{
578
+ \fBstring\fP
579
+ T}|T{
580
+
581
+ T}|T{
582
+ Working directory for the process
583
+ T}
584
+ _
585
+ T{
586
+ \fBenv\fP
587
+ T}|T{
588
+ \fBRecord<string, string>\fP
589
+ T}|T{
590
+
591
+ T}|T{
592
+ Extra environment variables\. Values support \fB$dep\.group\fP references from dependency capture groups\.
593
+ T}
594
+ _
595
+ T{
596
+ \fBenvFile\fP
597
+ T}|T{
598
+ \fBstring | string[] | false\fP
599
+ T}|T{
600
+
601
+ T}|T{
602
+ \|\.env file path(s) to load, or \fBfalse\fP to disable
603
+ T}
604
+ _
605
+ T{
606
+ \fBdependsOn\fP
607
+ T}|T{
608
+ \fBstring | string[]\fP
609
+ T}|T{
610
+
611
+ T}|T{
612
+ Processes that must be ready before this one starts
613
+ T}
614
+ _
615
+ T{
616
+ \fBreadyPattern\fP
617
+ T}|T{
618
+ \fBstring | RegExp\fP
619
+ T}|T{
620
+
621
+ T}|T{
622
+ Regex matched against stdout to signal readiness\. Use \fBRegExp\fP to capture groups for \fB$dep\.group\fP expansion
623
+ T}
624
+ _
625
+ T{
626
+ \fBmaxRestarts\fP
627
+ T}|T{
628
+ \fBnumber\fP
629
+ T}|T{
630
+ \fB0\fP
631
+ T}|T{
632
+ Limit auto\-restart attempts (only restarts on non\-zero exit)
633
+ T}
634
+ _
635
+ T{
636
+ \fBreadyTimeout\fP
637
+ T}|T{
638
+ \fBnumber\fP
639
+ T}|T{
640
+
641
+ T}|T{
642
+ Milliseconds to wait for readyPattern before failing
643
+ T}
644
+ _
645
+ T{
646
+ \fBdelay\fP
647
+ T}|T{
648
+ \fBnumber\fP
649
+ T}|T{
650
+
651
+ T}|T{
652
+ Milliseconds to wait before starting the process
653
+ T}
654
+ _
655
+ T{
656
+ \fBcondition\fP
657
+ T}|T{
658
+ \fBstring\fP
659
+ T}|T{
660
+
661
+ T}|T{
662
+ Env var name (prefix with \fB!\fP to negate); process skipped if condition is falsy
663
+ T}
664
+ _
665
+ T{
666
+ \fBplatform\fP
667
+ T}|T{
668
+ \fBstring | string[]\fP
669
+ T}|T{
670
+
671
+ T}|T{
672
+ OS(es) this process runs on (e\.g\. \fB&#39;darwin&#39;\fP, \fB&#39;linux&#39;\fP)\. Non\-matching processes are removed, their dependents still start
673
+ T}
674
+ _
675
+ T{
676
+ \fBstopSignal\fP
677
+ T}|T{
678
+ \fB&#39;SIGTERM&#39; | &#39;SIGINT&#39; | &#39;SIGHUP&#39;\fP
679
+ T}|T{
680
+ \fB&#39;SIGTERM&#39;\fP
681
+ T}|T{
682
+ Signal for graceful stop
683
+ T}
684
+ _
685
+ T{
686
+ \fBcolor\fP
687
+ T}|T{
688
+ \fBstring | string[]\fP
689
+ T}|T{
690
+
691
+ T}|T{
692
+ Hex color (e\.g\. \fB&quot;#ff6600&quot;\fP) or color name\. Array for round\-robin in script patterns
693
+ T}
694
+ _
695
+ T{
696
+ \fBwatch\fP
697
+ T}|T{
698
+ \fBstring | string[]\fP
699
+ T}|T{
700
+
701
+ T}|T{
702
+ Glob patterns — restart process when matching files change
703
+ T}
704
+ _
705
+ T{
706
+ \fBinteractive\fP
707
+ T}|T{
708
+ \fBboolean\fP
709
+ T}|T{
710
+ \fBfalse\fP
711
+ T}|T{
712
+ When true, keyboard input is forwarded to the process
713
+ T}
714
+ _
715
+ T{
716
+ \fBoptional\fP
717
+ T}|T{
718
+ \fBboolean\fP
719
+ T}|T{
720
+
721
+ T}|T{
722
+ Process is visible but not started automatically\. Use Alt+S to start manually
723
+ T}
724
+ _
725
+ T{
726
+ \fBerrorMatcher\fP
727
+ T}|T{
728
+ \fBboolean | string\fP
729
+ T}|T{
730
+
731
+ T}|T{
732
+ \fBtrue\fP = detect ANSI red output, string = regex pattern
733
+ T}
734
+ _
735
+ T{
736
+ \fBworkspaces\fP
737
+ T}|T{
738
+ \fBboolean | string | string[]\fP
739
+ T}|T{
740
+
741
+ T}|T{
742
+ Run command in monorepo workspaces\. \fBtrue\fP = all workspaces, string = specific workspace by name/path, string[] = multiple workspaces
743
+ T}
744
+ _
745
+ T{
746
+ \fBshowCommand\fP
747
+ T}|T{
748
+ \fBboolean\fP
749
+ T}|T{
750
+ \fBtrue\fP
751
+ T}|T{
752
+ Print the command being run as the first line of output
753
+ T}
754
+ .TE
755
+ <!\-\- /generated:config\-process \-\->
756
+
757
+ .SS Workspace expansion
758
+ .P
759
+ Use \fBworkspaces\fP on a process to expand it into per\-workspace processes\. Reads the \fBworkspaces\fP field from your root \fBpackage\.json\fP\|\.
760
+ .RS 2
761
+ .nf
762
+ export default defineConfig({
763
+ processes: {
764
+ // All workspaces — filters by script availability for PM run commands
765
+ lint: { command: 'npm run lint', workspaces: true },
766
+
767
+ // Specific workspace by package name
768
+ validate: { command: 'npm run validate', workspaces: '@repo/image\-worker' },
769
+
770
+ // Multiple specific workspaces
771
+ dev: { command: 'npm run dev', workspaces: ['@repo/api', '@repo/web'] },
772
+ },
773
+ })
774
+ .fi
775
+ .RE
776
+ .P
777
+ Each entry expands into \fB{name}:{wsName}\fP processes (e\.g\. \fBlint:api\fP, \fBlint:web\fP) with \fBcwd\fP set to the workspace directory\. All other config (env, dependsOn, color, etc\.) is inherited from the template\.
778
+ .P
779
+ When \fBworkspaces: true\fP is used with a PM run command (\fBnpm run lint\fP), only workspaces that have the matching script are included\. Raw commands (\fBeslint \.\fP) run in all workspaces\.
780
+ .P
781
+ String values resolve by package name first (with or without scope), then fall back to relative path\. Cannot be combined with \fBcwd\fP\|\.
782
+ .SS File watching
783
+ .P
784
+ Use \fBwatch\fP to automatically restart a process when source files change:
785
+ .RS 2
786
+ .nf
787
+ export default defineConfig({
788
+ processes: {
789
+ api: {
790
+ command: 'node server\.js',
791
+ watch: 'src/**/*\.ts',
792
+ },
793
+ styles: {
794
+ command: 'sass \-\-watch src:dist',
795
+ watch: ['src/**/*\.scss', 'src/**/*\.css'],
796
+ },
797
+ },
798
+ })
799
+ .fi
800
+ .RE
801
+ .P
802
+ Patterns are matched relative to the process's \fBcwd\fP (or the project root)\. Changes in \fBnode_modules\fP and \fB\|\.git\fP are always ignored\. Rapid file changes are debounced (300ms) to avoid restart storms\.
803
+ .P
804
+ A watched process is only restarted if it's currently running, ready, or failed — manually stopped processes are not affected\.
805
+ .SS Environment variable interpolation
806
+ .P
807
+ Config values support \fB${VAR}\fP syntax for environment variable substitution:
808
+ .RS 2
809
+ .nf
810
+ export default defineConfig({
811
+ processes: {
812
+ api: {
813
+ command: 'node server\.js \-\-port ${PORT:\-3000}',
814
+ env: {
815
+ DATABASE_URL: '${DATABASE_URL:?DATABASE_URL must be set}',
816
+ },
817
+ },
818
+ },
819
+ })
820
+ .fi
821
+ .RE
822
+ .TS
823
+ tab(|) expand nowarn box;
824
+ l l.
825
+ T{
826
+ Syntax
827
+ T}|T{
828
+ Behavior
829
+ T}
830
+ =
831
+ T{
832
+ \fB${VAR}\fP
833
+ T}|T{
834
+ Value of \fBVAR\fP, or empty string if unset
835
+ T}
836
+ _
837
+ T{
838
+ \fB${VAR:\-default}\fP
839
+ T}|T{
840
+ Value of \fBVAR\fP, or \fBdefault\fP if unset
841
+ T}
842
+ _
843
+ T{
844
+ \fB${VAR:?error}\fP
845
+ T}|T{
846
+ Value of \fBVAR\fP, or error with message if unset
847
+ T}
848
+ .TE
849
+ .P
850
+ Interpolation applies to all string values in the config (command, cwd, env, envFile, readyPattern, etc\.)\.
851
+ .SS Conditional processes
852
+ .P
853
+ Use \fBcondition\fP to run a process only when an environment variable is set:
854
+ .RS 2
855
+ .nf
856
+ export default defineConfig({
857
+ processes: {
858
+ seed: {
859
+ command: 'bun run seed',
860
+ condition: 'SEED_DB', // only runs when SEED_DB is set and truthy
861
+ },
862
+ storybook: {
863
+ command: 'bun run storybook',
864
+ condition: '!CI', // skipped in CI environments
865
+ },
866
+ },
867
+ })
868
+ .fi
869
+ .RE
870
+ .P
871
+ Falsy values: unset, empty string, \fB&quot;0&quot;\fP, \fB&quot;false&quot;\fP, \fB&quot;no&quot;\fP, \fB&quot;off&quot;\fP (case\-insensitive)\. If a conditional process is skipped, its dependents are also skipped\.
872
+ .SS Optional processes
873
+ .P
874
+ Use \fBoptional\fP for tools you want visible in tabs but not auto\-started (e\.g\. Prisma Studio, debug servers):
875
+ .RS 2
876
+ .nf
877
+ export default defineConfig({
878
+ processes: {
879
+ app: { command: 'bun run dev' },
880
+ studio: {
881
+ command: 'bunx prisma studio',
882
+ optional: true, // shows as stopped tab, start with Alt+S
883
+ },
884
+ },
885
+ })
886
+ .fi
887
+ .RE
888
+ .P
889
+ Unlike \fBcondition\fP, optional processes don't cascade — their dependents still start normally\.
890
+ .SS Dependency orchestration
891
+ .P
892
+ Each process starts as soon as its declared \fBdependsOn\fP dependencies are ready — it does not wait for unrelated processes\. If a process fails, its dependents are skipped\.
893
+ .P
894
+ A process becomes \fBready\fR when:
895
+
896
+ .RS 1
897
+ .IP \(bu 2
898
+ \fBHas \fBreadyPattern\fP\fR — the pattern matches in stdout (long\-running server)
899
+ .IP \(bu 2
900
+ \fBNo \fBreadyPattern\fP\fR — exits with code 0 (one\-shot task)
901
+
902
+ .RE
903
+ .P
904
+ Processes that crash (non\-zero exit) can be auto\-restarted by setting \fBmaxRestarts\fP (default: \fB0\fP)\. Restarts use exponential backoff (1s–30s), which resets after 10s of uptime\.
905
+ .SS Dependency output capture
906
+ .P
907
+ When \fBreadyPattern\fP is a \fBRegExp\fP (not a string), capture groups are extracted on match and expanded into dependent process \fBcommand\fP and \fBenv\fP values using \fB$process\.group\fP syntax:
908
+ .RS 2
909
+ .nf
910
+ export default defineConfig({
911
+ processes: {
912
+ db: {
913
+ command: 'docker compose up postgres',
914
+ readyPattern: /ready to accept connections on port (?<port>\\d+)/,
915
+ },
916
+ api: {
917
+ command: 'node server\.js \-\-db\-port $db\.port',
918
+ dependsOn: ['db'],
919
+ env: { DB_PORT: '$db\.port' },
920
+ },
921
+ },
922
+ })
923
+ .fi
924
+ .RE
925
+ .P
926
+ Both named (\fB$db\.port\fP) and positional (\fB$db\.1\fP) references work\. Named groups also populate positional slots, so \fB$db\.port\fP and \fB$db\.1\fP both resolve to the same value above\.
927
+ .P
928
+ Unmatched references are left as\-is (the shell will expand \fB$db\fP as empty + \fB\|\.port\fP literal, making the issue visible)\. String \fBreadyPattern\fP values work as before — readiness detection only, no capture extraction\.
929
+ .SH Keybindings
930
+ .P
931
+ Keybindings are shown in the status bar at the bottom of the app\. Panes are readonly by default — keyboard input is not forwarded to processes\. Set \fBinteractive: true\fP on processes that need stdin (REPLs, shells, etc\.)\.
932
+ <!\-\- generated:keybindings \-\->
933
+ .TS
934
+ tab(|) expand nowarn box;
935
+ l l.
936
+ T{
937
+ Key
938
+ T}|T{
939
+ Action
940
+ T}
941
+ =
942
+ T{
943
+ \fB←\fP/\fB→\fP or \fB1\fP\-\fB9\fP
944
+ T}|T{
945
+ Tabs
946
+ T}
947
+ _
948
+ T{
949
+ \fBG/Shift+G\fP
950
+ T}|T{
951
+ Top/bottom
952
+ T}
953
+ _
954
+ T{
955
+ \fBR\fP
956
+ T}|T{
957
+ Restart
958
+ T}
959
+ _
960
+ T{
961
+ \fBS\fP
962
+ T}|T{
963
+ Stop/start
964
+ T}
965
+ _
966
+ T{
967
+ \fBF\fP
968
+ T}|T{
969
+ Search
970
+ T}
971
+ _
972
+ T{
973
+ \fBY\fP
974
+ T}|T{
975
+ Copy all
976
+ T}
977
+ _
978
+ T{
979
+ \fBL\fP
980
+ T}|T{
981
+ Clear
982
+ T}
983
+ _
984
+ T{
985
+ \fBT\fP
986
+ T}|T{
987
+ Timestamps
988
+ T}
989
+ _
990
+ T{
991
+ \fBO\fP
992
+ T}|T{
993
+ Open logs
994
+ T}
995
+ _
996
+ T{
997
+ \fBCtrl+Click\fP
998
+ T}|T{
999
+ Open link
1000
+ T}
1001
+ _
1002
+ T{
1003
+ \fBCtrl+C\fP
1004
+ T}|T{
1005
+ Quit
1006
+ T}
1007
+ .TE
1008
+ <!\-\- /generated:keybindings \-\->
1009
+
1010
+ .P
1011
+ Search mode (after pressing \fBF\fP):
1012
+ .TS
1013
+ tab(|) expand nowarn box;
1014
+ l l.
1015
+ T{
1016
+ Key
1017
+ T}|T{
1018
+ Action
1019
+ T}
1020
+ =
1021
+ T{
1022
+ \fBTab\fP
1023
+ T}|T{
1024
+ Toggle between single\-pane and all\-process search
1025
+ T}
1026
+ _
1027
+ T{
1028
+ \fBEnter\fP/\fBShift+Enter\fP
1029
+ T}|T{
1030
+ Next/previous match
1031
+ T}
1032
+ _
1033
+ T{
1034
+ \fBEsc\fP
1035
+ T}|T{
1036
+ Exit search
1037
+ T}
1038
+ _
1039
+ T{
1040
+ \fBPageUp\fP/\fBPageDown\fP
1041
+ T}|T{
1042
+ Scroll by page
1043
+ T}
1044
+ .TE
1045
+ .SH Tab icons
1046
+ <!\-\- generated:tab\-icons \-\->
1047
+ .TS
1048
+ tab(|) expand nowarn box;
1049
+ l l.
1050
+ T{
1051
+ Icon
1052
+ T}|T{
1053
+ Status
1054
+ T}
1055
+ =
1056
+ T{
1057
+
1058
+ T}|T{
1059
+ Pending
1060
+ T}
1061
+ _
1062
+ T{
1063
+
1064
+ T}|T{
1065
+ Starting
1066
+ T}
1067
+ _
1068
+ T{
1069
+
1070
+ T}|T{
1071
+ Running
1072
+ T}
1073
+ _
1074
+ T{
1075
+
1076
+ T}|T{
1077
+ Ready
1078
+ T}
1079
+ _
1080
+ T{
1081
+
1082
+ T}|T{
1083
+ Stopping
1084
+ T}
1085
+ _
1086
+ T{
1087
+
1088
+ T}|T{
1089
+ Stopped
1090
+ T}
1091
+ _
1092
+ T{
1093
+
1094
+ T}|T{
1095
+ Finished
1096
+ T}
1097
+ _
1098
+ T{
1099
+
1100
+ T}|T{
1101
+ Failed
1102
+ T}
1103
+ _
1104
+ T{
1105
+
1106
+ T}|T{
1107
+ Skipped
1108
+ T}
1109
+ .TE
1110
+ <!\-\- /generated:tab\-icons \-\->
1111
+
1112
+ .SH Dependencies
1113
+ .SS ghostty\-opentui
1114
+ .P
1115
+ Despite the name,
1116
+ .UR https://github.com/remorses/ghostty-opentui
1117
+ .I `ghostty-opentui`
1118
+ .UE
1119
+ is \fBnot\fR a compatibility layer for the
1120
+ .UR https://ghostty.org
1121
+ .I Ghostty
1122
+ .UE
1123
+ terminal\. It uses Ghostty's Zig\-based VT parser as the ANSI terminal emulation engine for OpenTUI's terminal renderable\. It works in any terminal emulator (iTerm, Kitty, Alacritty, WezTerm, etc\.) and adds ~8MB to install size due to native binaries\.
1124
+ .SH License
1125
+ .P
1126
+ MIT
1127
+