@pablozaiden/terminatui 0.3.0-beta-1 → 0.3.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.
- package/package.json +10 -3
- package/src/__tests__/configOnChange.test.ts +63 -0
- package/src/builtins/version.ts +1 -1
- package/src/index.ts +22 -0
- package/src/tui/adapters/ink/InkRenderer.tsx +4 -0
- package/src/tui/adapters/opentui/OpenTuiRenderer.tsx +4 -0
- package/src/tui/adapters/types.ts +1 -0
- package/src/tui/screens/ConfigScreen.tsx +6 -1
- package/src/tui/screens/ResultsScreen.tsx +9 -1
- package/src/tui/screens/RunningScreen.tsx +1 -1
- package/.devcontainer/devcontainer.json +0 -19
- package/.devcontainer/install-prerequisites.sh +0 -49
- package/.github/workflows/copilot-setup-steps.yml +0 -32
- package/.github/workflows/pull-request.yml +0 -27
- package/.github/workflows/release-npm-package.yml +0 -81
- package/AGENTS.md +0 -43
- package/CLAUDE.md +0 -1
- package/bun.lock +0 -321
- package/examples/tui-app/commands/config/app/get.ts +0 -62
- package/examples/tui-app/commands/config/app/index.ts +0 -23
- package/examples/tui-app/commands/config/app/set.ts +0 -96
- package/examples/tui-app/commands/config/index.ts +0 -28
- package/examples/tui-app/commands/config/user/get.ts +0 -61
- package/examples/tui-app/commands/config/user/index.ts +0 -23
- package/examples/tui-app/commands/config/user/set.ts +0 -57
- package/examples/tui-app/commands/greet.ts +0 -78
- package/examples/tui-app/commands/math.ts +0 -111
- package/examples/tui-app/commands/status.ts +0 -86
- package/examples/tui-app/index.ts +0 -38
- package/guides/01-hello-world.md +0 -101
- package/guides/02-adding-options.md +0 -103
- package/guides/03-multiple-commands.md +0 -161
- package/guides/04-subcommands.md +0 -206
- package/guides/05-interactive-tui.md +0 -209
- package/guides/06-config-validation.md +0 -256
- package/guides/07-async-cancellation.md +0 -334
- package/guides/08-complete-application.md +0 -507
- package/guides/README.md +0 -78
- package/tsconfig.json +0 -25
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
# Guide 3: Multiple Commands (Basic)
|
|
2
|
-
|
|
3
|
-
Build a CLI with multiple commands that share common functionality.
|
|
4
|
-
|
|
5
|
-
## What You'll Build
|
|
6
|
-
|
|
7
|
-
A file utility CLI with `list` and `count` commands:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
fileutil list --dir ./src
|
|
11
|
-
# Lists files in directory
|
|
12
|
-
|
|
13
|
-
fileutil count --dir ./src --ext .ts
|
|
14
|
-
# Counts files with extension
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Step 1: Create the List Command
|
|
18
|
-
|
|
19
|
-
Create `src/commands/list.ts`:
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
|
|
23
|
-
|
|
24
|
-
const options = {
|
|
25
|
-
dir: {
|
|
26
|
-
type: "string",
|
|
27
|
-
description: "Directory to list",
|
|
28
|
-
required: true,
|
|
29
|
-
alias: "d",
|
|
30
|
-
},
|
|
31
|
-
} satisfies OptionSchema;
|
|
32
|
-
|
|
33
|
-
export class ListCommand extends Command<typeof options> {
|
|
34
|
-
readonly name = "list";
|
|
35
|
-
readonly description = "List files in a directory";
|
|
36
|
-
readonly options = options;
|
|
37
|
-
|
|
38
|
-
async execute(config: { dir: string }): Promise<CommandResult> {
|
|
39
|
-
try {
|
|
40
|
-
const files = await Array.fromAsync(new Bun.Glob("*").scan({ cwd: config.dir, onlyFiles: false }));
|
|
41
|
-
console.log(`Files in ${config.dir}:`);
|
|
42
|
-
files.forEach((file) => console.log(` ${file}`));
|
|
43
|
-
return { success: true, message: `Found ${files.length} files` };
|
|
44
|
-
} catch (error) {
|
|
45
|
-
return { success: false, error: String(error) };
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Step 2: Create the Count Command
|
|
52
|
-
|
|
53
|
-
Create `src/commands/count.ts`:
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
|
|
57
|
-
|
|
58
|
-
const options = {
|
|
59
|
-
dir: {
|
|
60
|
-
type: "string",
|
|
61
|
-
description: "Directory to search",
|
|
62
|
-
required: true,
|
|
63
|
-
alias: "d",
|
|
64
|
-
},
|
|
65
|
-
ext: {
|
|
66
|
-
type: "string",
|
|
67
|
-
description: "File extension to count (e.g., .ts)",
|
|
68
|
-
alias: "e",
|
|
69
|
-
},
|
|
70
|
-
} satisfies OptionSchema;
|
|
71
|
-
|
|
72
|
-
export class CountCommand extends Command<typeof options> {
|
|
73
|
-
readonly name = "count";
|
|
74
|
-
readonly description = "Count files in a directory";
|
|
75
|
-
readonly options = options;
|
|
76
|
-
|
|
77
|
-
async execute(config: { dir: string; ext?: string }): Promise<CommandResult> {
|
|
78
|
-
try {
|
|
79
|
-
let files = await Array.fromAsync(new Bun.Glob("*").scan({ cwd: config.dir, onlyFiles: false }));
|
|
80
|
-
|
|
81
|
-
if (config.ext) {
|
|
82
|
-
files = files.filter((f) => f.endsWith(config.ext!));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
console.log(`Found ${files.length} files${config.ext ? ` with ${config.ext}` : ""}`);
|
|
86
|
-
return { success: true, data: { count: files.length } };
|
|
87
|
-
} catch (error) {
|
|
88
|
-
return { success: false, error: String(error) };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Step 3: Create the Application
|
|
95
|
-
|
|
96
|
-
Create `src/index.ts`:
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
import { Application } from "@pablozaiden/terminatui";
|
|
100
|
-
import { ListCommand } from "./commands/list";
|
|
101
|
-
import { CountCommand } from "./commands/count";
|
|
102
|
-
|
|
103
|
-
class FileUtilApp extends Application {
|
|
104
|
-
constructor() {
|
|
105
|
-
super({
|
|
106
|
-
name: "fileutil",
|
|
107
|
-
version: "1.0.0",
|
|
108
|
-
description: "File utility commands",
|
|
109
|
-
commands: [
|
|
110
|
-
new ListCommand(),
|
|
111
|
-
new CountCommand(),
|
|
112
|
-
],
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
await new FileUtilApp().run();
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Step 4: Test It
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# List files
|
|
124
|
-
bun src/index.ts list --dir ./src
|
|
125
|
-
# Files in ./src:
|
|
126
|
-
# commands
|
|
127
|
-
# index.ts
|
|
128
|
-
|
|
129
|
-
# Count all files
|
|
130
|
-
bun src/index.ts count -d ./src
|
|
131
|
-
# Found 2 files
|
|
132
|
-
|
|
133
|
-
# Count only .ts files
|
|
134
|
-
bun src/index.ts count -d ./src -e .ts
|
|
135
|
-
# Found 1 files with .ts
|
|
136
|
-
|
|
137
|
-
# Show help
|
|
138
|
-
bun src/index.ts help
|
|
139
|
-
# Lists: list, count, version, help
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Project Structure
|
|
143
|
-
|
|
144
|
-
```
|
|
145
|
-
src/
|
|
146
|
-
├── index.ts # App entry point
|
|
147
|
-
└── commands/
|
|
148
|
-
├── list.ts # List command
|
|
149
|
-
└── count.ts # Count command
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## What You Learned
|
|
153
|
-
|
|
154
|
-
- Create multiple commands in separate files
|
|
155
|
-
- Return structured `CommandResult` with data
|
|
156
|
-
- Handle errors gracefully
|
|
157
|
-
- Use consistent option patterns across commands
|
|
158
|
-
|
|
159
|
-
## Next Steps
|
|
160
|
-
|
|
161
|
-
→ [Guide 4: Subcommands](04-subcommands.md)
|
package/guides/04-subcommands.md
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# Guide 4: Subcommands (Basic)
|
|
2
|
-
|
|
3
|
-
Organize related commands under a parent command for cleaner CLI structure.
|
|
4
|
-
|
|
5
|
-
## What You'll Build
|
|
6
|
-
|
|
7
|
-
A database CLI with nested subcommands:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
dbctl db migrate --target latest
|
|
11
|
-
dbctl db seed --file data.json
|
|
12
|
-
dbctl db status
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Step 1: Create the Subcommands
|
|
16
|
-
|
|
17
|
-
Create `src/commands/db/migrate.ts`:
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
|
|
21
|
-
|
|
22
|
-
const options = {
|
|
23
|
-
target: {
|
|
24
|
-
type: "string",
|
|
25
|
-
description: "Migration target version",
|
|
26
|
-
default: "latest",
|
|
27
|
-
},
|
|
28
|
-
dry: {
|
|
29
|
-
type: "boolean",
|
|
30
|
-
description: "Dry run without applying",
|
|
31
|
-
default: false,
|
|
32
|
-
},
|
|
33
|
-
} satisfies OptionSchema;
|
|
34
|
-
|
|
35
|
-
export class MigrateCommand extends Command<typeof options> {
|
|
36
|
-
readonly name = "migrate";
|
|
37
|
-
readonly description = "Run database migrations";
|
|
38
|
-
readonly options = options;
|
|
39
|
-
|
|
40
|
-
execute(config: { target: string; dry: boolean }): CommandResult {
|
|
41
|
-
ctx.logger.info(`Migrating to: ${config.target}`);
|
|
42
|
-
|
|
43
|
-
if (config.dry) {
|
|
44
|
-
console.log("DRY RUN: Would migrate to", config.target);
|
|
45
|
-
} else {
|
|
46
|
-
console.log("Migrating to", config.target, "...");
|
|
47
|
-
console.log("Migration complete!");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return { success: true };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Create `src/commands/db/seed.ts`:
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { Command, type OptionSchema, type CommandResult } from "@pablozaiden/terminatui";
|
|
59
|
-
|
|
60
|
-
const options = {
|
|
61
|
-
file: {
|
|
62
|
-
type: "string",
|
|
63
|
-
description: "Seed data file",
|
|
64
|
-
required: true,
|
|
65
|
-
alias: "f",
|
|
66
|
-
},
|
|
67
|
-
} satisfies OptionSchema;
|
|
68
|
-
|
|
69
|
-
export class SeedCommand extends Command<typeof options> {
|
|
70
|
-
readonly name = "seed";
|
|
71
|
-
readonly description = "Seed the database with data";
|
|
72
|
-
readonly options = options;
|
|
73
|
-
|
|
74
|
-
execute(config: { file: string }): CommandResult {
|
|
75
|
-
ctx.logger.info(`Seeding from: ${config.file}`);
|
|
76
|
-
console.log(`Loading seed data from ${config.file}...`);
|
|
77
|
-
console.log("Database seeded successfully!");
|
|
78
|
-
return { success: true };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Create `src/commands/db/status.ts`:
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
import { Command, type CommandResult } from "@pablozaiden/terminatui";
|
|
87
|
-
|
|
88
|
-
export class StatusCommand extends Command {
|
|
89
|
-
readonly name = "status";
|
|
90
|
-
readonly description = "Show database status";
|
|
91
|
-
readonly options = {};
|
|
92
|
-
|
|
93
|
-
execute(): CommandResult {
|
|
94
|
-
console.log("Database Status:");
|
|
95
|
-
console.log(" Connected: Yes");
|
|
96
|
-
console.log(" Version: 1.2.3");
|
|
97
|
-
console.log(" Migrations: 5 applied");
|
|
98
|
-
return { success: true };
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Step 2: Create the Parent Command
|
|
104
|
-
|
|
105
|
-
Create `src/commands/db/index.ts`:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { Command, type CommandResult } from "@pablozaiden/terminatui";
|
|
109
|
-
import { MigrateCommand } from "./migrate";
|
|
110
|
-
import { SeedCommand } from "./seed";
|
|
111
|
-
import { StatusCommand } from "./status";
|
|
112
|
-
|
|
113
|
-
export class DbCommand extends Command {
|
|
114
|
-
readonly name = "db";
|
|
115
|
-
readonly description = "Database operations";
|
|
116
|
-
readonly options = {};
|
|
117
|
-
|
|
118
|
-
// Subcommands are nested here
|
|
119
|
-
override readonly subCommands = [
|
|
120
|
-
new MigrateCommand(),
|
|
121
|
-
new SeedCommand(),
|
|
122
|
-
new StatusCommand(),
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
// Parent command can have its own execute (optional)
|
|
126
|
-
execute(): CommandResult {
|
|
127
|
-
console.log("Use 'dbctl db <command>' for database operations.");
|
|
128
|
-
console.log("Available: migrate, seed, status");
|
|
129
|
-
return { success: true };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Step 3: Create the Application
|
|
135
|
-
|
|
136
|
-
Create `src/index.ts`:
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
import { Application } from "@pablozaiden/terminatui";
|
|
140
|
-
import { DbCommand } from "./commands/db";
|
|
141
|
-
|
|
142
|
-
class DbCtlApp extends Application {
|
|
143
|
-
constructor() {
|
|
144
|
-
super({
|
|
145
|
-
name: "dbctl",
|
|
146
|
-
version: "1.0.0",
|
|
147
|
-
description: "Database control CLI",
|
|
148
|
-
commands: [new DbCommand()],
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
await new DbCtlApp().run();
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## Step 4: Test It
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
# Show db command help
|
|
160
|
-
bun src/index.ts db help
|
|
161
|
-
# Shows: migrate, seed, status
|
|
162
|
-
|
|
163
|
-
# Run migration
|
|
164
|
-
bun src/index.ts db migrate
|
|
165
|
-
# Migrating to latest...
|
|
166
|
-
|
|
167
|
-
# Dry run migration
|
|
168
|
-
bun src/index.ts db migrate --target v2 --dry
|
|
169
|
-
# DRY RUN: Would migrate to v2
|
|
170
|
-
|
|
171
|
-
# Seed database
|
|
172
|
-
bun src/index.ts db seed -f data.json
|
|
173
|
-
# Loading seed data from data.json...
|
|
174
|
-
|
|
175
|
-
# Check status
|
|
176
|
-
bun src/index.ts db status
|
|
177
|
-
# Database Status: ...
|
|
178
|
-
|
|
179
|
-
# Get help for subcommand
|
|
180
|
-
bun src/index.ts db migrate help
|
|
181
|
-
# Shows migrate options
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Project Structure
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
src/
|
|
188
|
-
├── index.ts
|
|
189
|
-
└── commands/
|
|
190
|
-
└── db/
|
|
191
|
-
├── index.ts # Parent command
|
|
192
|
-
├── migrate.ts # Subcommand
|
|
193
|
-
├── seed.ts # Subcommand
|
|
194
|
-
└── status.ts # Subcommand
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## What You Learned
|
|
198
|
-
|
|
199
|
-
- Group related commands under a parent
|
|
200
|
-
- Define subcommands with `subCommands` property
|
|
201
|
-
- Each subcommand has its own options
|
|
202
|
-
- Help is automatically nested (`db help`, `db migrate help`)
|
|
203
|
-
|
|
204
|
-
## Next Steps
|
|
205
|
-
|
|
206
|
-
→ [Guide 5: Interactive TUI](05-interactive-tui.md)
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# Guide 5: Interactive TUI
|
|
2
|
-
|
|
3
|
-
Add an auto-generated Terminal User Interface to your CLI.
|
|
4
|
-
|
|
5
|
-
## What You'll Build
|
|
6
|
-
|
|
7
|
-
A task runner with both CLI and interactive TUI modes:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# CLI mode
|
|
11
|
-
taskr --mode cli run --task build --env production
|
|
12
|
-
|
|
13
|
-
# TUI mode
|
|
14
|
-
# (if your app's default mode is a TUI mode)
|
|
15
|
-
taskr
|
|
16
|
-
|
|
17
|
-
# Force TUI mode
|
|
18
|
-
taskr --mode opentui
|
|
19
|
-
# or
|
|
20
|
-
taskr --mode ink
|
|
21
|
-
|
|
22
|
-
# Force CLI mode
|
|
23
|
-
taskr --mode cli
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Only the selected mode (`--mode`) or your app's default mode controls whether the app runs in CLI or TUI.
|
|
27
|
-
|
|
28
|
-
## Step 1: Create the Command with TUI Metadata
|
|
29
|
-
|
|
30
|
-
Create `src/commands/run.ts`:
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
import {
|
|
34
|
-
Command,
|
|
35
|
-
type OptionSchema,
|
|
36
|
-
type CommandResult,
|
|
37
|
-
type CommandExecutionContext,
|
|
38
|
-
} from "@pablozaiden/terminatui";
|
|
39
|
-
|
|
40
|
-
const options = {
|
|
41
|
-
task: {
|
|
42
|
-
type: "string",
|
|
43
|
-
description: "Task to run",
|
|
44
|
-
required: true,
|
|
45
|
-
enum: ["build", "test", "lint", "deploy"],
|
|
46
|
-
// TUI metadata
|
|
47
|
-
label: "Task",
|
|
48
|
-
order: 1,
|
|
49
|
-
group: "Required",
|
|
50
|
-
},
|
|
51
|
-
env: {
|
|
52
|
-
type: "string",
|
|
53
|
-
description: "Environment",
|
|
54
|
-
default: "development",
|
|
55
|
-
enum: ["development", "staging", "production"],
|
|
56
|
-
// TUI metadata
|
|
57
|
-
label: "Environment",
|
|
58
|
-
order: 2,
|
|
59
|
-
group: "Configuration",
|
|
60
|
-
},
|
|
61
|
-
verbose: {
|
|
62
|
-
type: "boolean",
|
|
63
|
-
description: "Verbose output",
|
|
64
|
-
default: false,
|
|
65
|
-
// TUI metadata
|
|
66
|
-
label: "Verbose Mode",
|
|
67
|
-
order: 10,
|
|
68
|
-
group: "Options",
|
|
69
|
-
},
|
|
70
|
-
} satisfies OptionSchema;
|
|
71
|
-
|
|
72
|
-
interface RunConfig {
|
|
73
|
-
task: string;
|
|
74
|
-
env: string;
|
|
75
|
-
verbose: boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export class RunCommand extends Command<typeof options, RunConfig> {
|
|
79
|
-
readonly name = "run";
|
|
80
|
-
readonly description = "Run a task";
|
|
81
|
-
readonly options = options;
|
|
82
|
-
|
|
83
|
-
// TUI customization
|
|
84
|
-
override readonly displayName = "Run Task";
|
|
85
|
-
override readonly actionLabel = "Start Task";
|
|
86
|
-
|
|
87
|
-
async execute(config: RunConfig, _execCtx: CommandExecutionContext): Promise<CommandResult> {
|
|
88
|
-
if (config.verbose) {
|
|
89
|
-
console.debug(`Environment: ${config.env}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Simulate task execution
|
|
93
|
-
console.log(`Running ${config.task} in ${config.env}...`);
|
|
94
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
95
|
-
console.log("Task completed!");
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
success: true,
|
|
99
|
-
data: { task: config.task, env: config.env },
|
|
100
|
-
message: `Task ${config.task} completed successfully`,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Step 2: Create the TUI Application
|
|
107
|
-
|
|
108
|
-
Create `src/index.ts`:
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
import { TuiApplication } from "@pablozaiden/terminatui";
|
|
112
|
-
import { RunCommand } from "./commands/run";
|
|
113
|
-
|
|
114
|
-
class TaskRunnerApp extends TuiApplication {
|
|
115
|
-
// Default is CLI; each app decides.
|
|
116
|
-
protected override defaultMode = "opentui" as const;
|
|
117
|
-
|
|
118
|
-
constructor() {
|
|
119
|
-
super({
|
|
120
|
-
name: "taskr",
|
|
121
|
-
displayName: "🚀 Task Runner", // Shown in TUI header
|
|
122
|
-
version: "1.0.0",
|
|
123
|
-
commands: [new RunCommand()],
|
|
124
|
-
enableTui: true, // Default: true
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
await new TaskRunnerApp().run();
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Step 3: Test Both Modes
|
|
133
|
-
|
|
134
|
-
**CLI Mode** (forced):
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
bun src/index.ts --mode cli run --task build --env production
|
|
138
|
-
# Running build in production...
|
|
139
|
-
# Task completed!
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**TUI Mode** (forced):
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
bun src/index.ts --mode opentui
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
This opens an interactive interface:
|
|
149
|
-
- Use ↑/↓ to navigate fields
|
|
150
|
-
- Press Enter to edit a field
|
|
151
|
-
- Navigate to "CLI Command" button and press Enter to see the CLI command
|
|
152
|
-
- Press Enter on "Start Task" to run
|
|
153
|
-
- Press Esc to go back
|
|
154
|
-
|
|
155
|
-
## TUI Metadata Reference
|
|
156
|
-
|
|
157
|
-
Add these properties to your options for TUI customization:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
{
|
|
161
|
-
type: "string",
|
|
162
|
-
description: "...",
|
|
163
|
-
|
|
164
|
-
// TUI-specific
|
|
165
|
-
label: "Display Label", // Custom field label
|
|
166
|
-
order: 1, // Field sort order
|
|
167
|
-
group: "Settings", // Group heading
|
|
168
|
-
placeholder: "Enter...", // Placeholder text
|
|
169
|
-
tuiHidden: false, // Hide from TUI (still in CLI)
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## Command TUI Properties
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
class MyCommand extends Command {
|
|
177
|
-
// Display name in command selector
|
|
178
|
-
override readonly displayName = "My Command";
|
|
179
|
-
|
|
180
|
-
// Button text (default: "Run")
|
|
181
|
-
override readonly actionLabel = "Execute";
|
|
182
|
-
|
|
183
|
-
// Skip config screen, run immediately
|
|
184
|
-
override readonly immediateExecution = false;
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Keyboard Shortcuts
|
|
189
|
-
|
|
190
|
-
| Key | Action |
|
|
191
|
-
|-----|--------|
|
|
192
|
-
| ↑/↓ | Navigate fields |
|
|
193
|
-
| Enter | Edit field / Press button / Run |
|
|
194
|
-
| Tab | Cycle focus |
|
|
195
|
-
| L | Toggle logs |
|
|
196
|
-
| Ctrl+Y | Copy to clipboard |
|
|
197
|
-
| Esc | Back / Cancel |
|
|
198
|
-
|
|
199
|
-
## What You Learned
|
|
200
|
-
|
|
201
|
-
- Use `TuiApplication` instead of `Application`
|
|
202
|
-
- Use `--mode` (or app default mode) to control CLI vs TUI
|
|
203
|
-
- Add TUI metadata to options (label, order, group)
|
|
204
|
-
- Customize with `displayName` and `actionLabel`
|
|
205
|
-
- Both CLI and TUI work with the same command
|
|
206
|
-
|
|
207
|
-
## Next Steps
|
|
208
|
-
|
|
209
|
-
→ [Guide 6: Config Validation](06-config-validation.md)
|