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