0
# Command Development System
1
2
The command development system provides the foundation for creating individual CLI commands with arguments, options, and business logic. Commands inherit from base classes and define their interface through class attributes and method implementations.
3
4
## Capabilities
5
6
### Base Command Structure
7
8
Abstract base class that defines the fundamental command interface and lifecycle.
9
10
```python { .api }
11
class BaseCommand:
12
name: str | None = None # Command name (required)
13
description: str = "" # Short description for help
14
help: str = "" # Detailed help text
15
enabled: bool = True # Whether command is enabled
16
hidden: bool = False # Whether to hide from command lists
17
18
def configure(self) -> None:
19
"""Configure the command definition. Called during initialization."""
20
21
def execute(self, io: IO) -> int:
22
"""
23
Execute the command with provided IO.
24
25
Args:
26
io (IO): Input/output interface
27
28
Returns:
29
int: Exit code (0 for success)
30
"""
31
32
def run(self, io: IO) -> int:
33
"""
34
Run the command through the full lifecycle.
35
36
Args:
37
io (IO): Input/output interface
38
39
Returns:
40
int: Exit code from execution
41
"""
42
```
43
44
### Full-Featured Command Class
45
46
Complete command implementation with UI helpers and convenience methods.
47
48
```python { .api }
49
class Command(BaseCommand):
50
arguments: ClassVar[list[Argument]] = [] # Command arguments definition
51
options: ClassVar[list[Option]] = [] # Command options definition
52
aliases: ClassVar[list[str]] = [] # Command aliases
53
usages: ClassVar[list[str]] = [] # Usage examples
54
commands: ClassVar[list[BaseCommand]] = [] # Sub-commands
55
56
def __init__(self) -> None: ...
57
58
@property
59
def io(self) -> IO:
60
"""Get the IO interface for this command."""
61
62
def handle(self) -> int:
63
"""
64
Command business logic implementation (abstract).
65
66
Returns:
67
int: Exit code (0 for success)
68
"""
69
70
# Command execution methods
71
def call(self, name: str, args: str | None = None) -> int: ...
72
def call_silent(self, name: str, args: str | None = None) -> int: ...
73
74
# User interaction methods
75
def confirm(self, question: str, default: bool = False, true_answer_regex: str = r"(?i)^y") -> bool: ...
76
def ask(self, question: str | Question, default: Any | None = None) -> Any: ...
77
def secret(self, question: str | Question, default: Any | None = None) -> Any: ...
78
def choice(self, question: str, choices: list[str], default: Any | None = None,
79
attempts: int | None = None, multiple: bool = False) -> Any: ...
80
81
# Output methods
82
def write(self, text: str, style: str | None = None) -> None: ...
83
def line_error(self, text: str, style: str | None = None, verbosity: Verbosity = Verbosity.NORMAL) -> None: ...
84
def info(self, text: str) -> None: ...
85
def comment(self, text: str) -> None: ...
86
def question(self, text: str) -> None: ...
87
def overwrite(self, text: str) -> None: ...
88
89
# Table rendering
90
def table(self, header: str | None = None, rows: Rows | None = None, style: str | None = None) -> Table: ...
91
def table_separator(self) -> TableSeparator: ...
92
def render_table(self, headers: str, rows: Rows, style: str | None = None) -> None: ...
93
94
# Progress indicators
95
def progress_bar(self, max: int = 0) -> ProgressBar: ...
96
def progress_indicator(self, fmt: str | None = None, interval: int = 100, values: list[str] | None = None) -> ProgressIndicator: ...
97
def spin(self, start_message: str, end_message: str, fmt: str | None = None,
98
interval: int = 100, values: list[str] | None = None) -> ContextManager[ProgressIndicator]: ...
99
100
# Styling
101
def add_style(self, name: str, fg: str | None = None, bg: str | None = None, options: list[str] | None = None) -> None: ...
102
103
# Utility
104
def create_question(self, question: str, type: Literal["choice", "confirmation"] | None = None, **kwargs: Any) -> Question: ...
105
```
106
107
### Argument and Option Access
108
109
Access command arguments and options provided by the user.
110
111
```python { .api }
112
def argument(self, name: str) -> Any:
113
"""
114
Get an argument value by name.
115
116
Args:
117
name (str): Argument name
118
119
Returns:
120
Any: Argument value or None if not provided
121
"""
122
123
def option(self, name: str) -> Any:
124
"""
125
Get an option value by name.
126
127
Args:
128
name (str): Option name
129
130
Returns:
131
Any: Option value, default value, or None
132
"""
133
```
134
135
### Argument and Option Definition Helpers
136
137
Helper functions to create argument and option definitions for commands.
138
139
```python { .api }
140
def argument(name: str, description: str | None = None, optional: bool = False,
141
multiple: bool = False, default: Any | None = None) -> Argument:
142
"""
143
Create an argument definition.
144
145
Args:
146
name (str): Argument name
147
description (str | None): Help description
148
optional (bool): Whether argument is optional
149
multiple (bool): Whether argument accepts multiple values
150
default (Any | None): Default value for optional arguments
151
152
Returns:
153
Argument: Configured argument definition
154
"""
155
156
def option(long_name: str, short_name: str | None = None, description: str | None = None,
157
flag: bool = True, value_required: bool = True, multiple: bool = False,
158
default: Any | None = None) -> Option:
159
"""
160
Create an option definition.
161
162
Args:
163
long_name (str): Long option name (--option)
164
short_name (str | None): Short option name (-o)
165
description (str | None): Help description
166
flag (bool): Whether this is a boolean flag
167
value_required (bool): Whether option requires a value
168
multiple (bool): Whether option accepts multiple values
169
default (Any | None): Default value
170
171
Returns:
172
Option: Configured option definition
173
"""
174
```
175
176
### Output and Interaction Methods
177
178
Methods for producing output and interacting with users.
179
180
```python { .api }
181
def line(self, text: str, style: str | None = None, verbosity: Verbosity = Verbosity.NORMAL) -> None:
182
"""
183
Write a line of text to output.
184
185
Args:
186
text (str): Text to output
187
style (str | None): Style name for formatting
188
verbosity (Verbosity | None): Minimum verbosity level
189
"""
190
191
def write(self, text: str, new_line: bool = False, verbosity: Verbosity | None = None) -> None:
192
"""
193
Write text to output.
194
195
Args:
196
text (str): Text to output
197
new_line (bool): Whether to add a newline
198
verbosity (Verbosity | None): Minimum verbosity level
199
"""
200
201
def confirm(self, question: str, default: bool = False) -> bool:
202
"""
203
Ask a yes/no confirmation question.
204
205
Args:
206
question (str): Question text
207
default (bool): Default answer
208
209
Returns:
210
bool: User's answer
211
"""
212
213
def ask(self, question: str | Question, default: Any | None = None) -> Any:
214
"""
215
Ask a question and get user input.
216
217
Args:
218
question (str | Question): Question text or Question object
219
default (Any | None): Default answer
220
221
Returns:
222
Any: User's answer
223
"""
224
225
def choice(self, question: str, choices: list[str], default: Any | None = None) -> Any:
226
"""
227
Ask a multiple choice question.
228
229
Args:
230
question (str): Question text
231
choices (list[str]): Available choices
232
default (Any | None): Default choice
233
234
Returns:
235
Any: Selected choice
236
"""
237
```
238
239
### Command Execution and Control Flow
240
241
Methods for controlling command execution and calling other commands.
242
243
```python { .api }
244
def call(self, command: str, args: str = "") -> int:
245
"""
246
Call another command from this command.
247
248
Args:
249
command (str): Command name to call
250
args (str): Arguments to pass to the command
251
252
Returns:
253
int: Exit code from called command
254
"""
255
256
def call_silent(self, command: str, args: str = "") -> int:
257
"""
258
Call another command silently (suppressing output).
259
260
Args:
261
command (str): Command name to call
262
args (str): Arguments to pass
263
264
Returns:
265
int: Exit code from called command
266
"""
267
```
268
269
### Style and Formatting Methods
270
271
Methods for adding custom styles and formatting output.
272
273
```python { .api }
274
def add_style(self, name: str, fg: str | None = None, bg: str | None = None,
275
options: list[str] | None = None) -> None:
276
"""
277
Add a custom style for text formatting.
278
279
Args:
280
name (str): Style name
281
fg (str | None): Foreground color
282
bg (str | None): Background color
283
options (list[str] | None): Formatting options (bold, underscore, etc.)
284
"""
285
```
286
287
## Input Definition Components
288
289
### Argument Class
290
291
Defines command arguments with validation and type information.
292
293
```python { .api }
294
class Argument:
295
def __init__(self, name: str, required: bool = True, is_list: bool = False,
296
description: str | None = None, default: Any | None = None): ...
297
298
@property
299
def name(self) -> str: ...
300
301
@property
302
def is_required(self) -> bool: ...
303
304
@property
305
def is_optional(self) -> bool: ...
306
307
@property
308
def is_list(self) -> bool: ...
309
310
@property
311
def description(self) -> str: ...
312
313
@property
314
def default(self) -> Any: ...
315
```
316
317
### Option Class
318
319
Defines command options with flags, values, and validation.
320
321
```python { .api }
322
class Option:
323
def __init__(self, name: str, shortcut: str | None = None, flag: bool = True,
324
requires_value: bool = True, is_list: bool = False,
325
description: str | None = None, default: Any | None = None): ...
326
327
@property
328
def name(self) -> str: ...
329
330
@property
331
def shortcut(self) -> str: ...
332
333
@property
334
def is_flag(self) -> bool: ...
335
336
@property
337
def requires_value(self) -> bool: ...
338
339
@property
340
def is_list(self) -> bool: ...
341
342
@property
343
def description(self) -> str: ...
344
345
@property
346
def default(self) -> Any: ...
347
```
348
349
## Usage Examples
350
351
### Basic Command
352
353
```python
354
from cleo.commands.command import Command
355
from cleo.helpers import argument, option
356
357
class ProcessCommand(Command):
358
name = "process"
359
description = "Process data files"
360
arguments = [
361
argument("input_file", description="Input file path"),
362
argument("output_file", description="Output file path", optional=True)
363
]
364
options = [
365
option("format", "f", description="Output format", flag=False, default="json"),
366
option("verbose", "v", description="Verbose output", flag=True)
367
]
368
369
def handle(self):
370
input_file = self.argument("input_file")
371
output_file = self.argument("output_file") or f"{input_file}.processed"
372
format = self.option("format")
373
verbose = self.option("verbose")
374
375
if verbose:
376
self.line(f"Processing <info>{input_file}</info>")
377
378
# Process the file
379
result = self.process_file(input_file, format)
380
381
# Write output
382
with open(output_file, 'w') as f:
383
f.write(result)
384
385
self.line(f"<comment>Processed data written to {output_file}</comment>")
386
387
return 0
388
389
def process_file(self, file_path, format):
390
# Implementation here
391
return "processed data"
392
```
393
394
### Interactive Command with Questions
395
396
```python
397
class ConfigCommand(Command):
398
name = "config"
399
description = "Configure application settings"
400
401
def handle(self):
402
# Get configuration values interactively
403
host = self.ask("Database host", "localhost")
404
port = self.ask("Database port", 5432)
405
406
# Confirm settings
407
if self.confirm(f"Connect to {host}:{port}?", True):
408
# Multiple choice for environment
409
env = self.choice(
410
"Environment",
411
["development", "staging", "production"],
412
"development"
413
)
414
415
self.line(f"<info>Configuration saved for {env} environment</info>")
416
else:
417
self.line("<error>Configuration cancelled</error>")
418
return 1
419
420
return 0
421
```
422
423
### Command with Sub-commands
424
425
```python
426
class DatabaseCommand(Command):
427
name = "db"
428
description = "Database management commands"
429
430
def handle(self):
431
# Show available sub-commands
432
self.line("Available database commands:")
433
self.line(" db:migrate - Run database migrations")
434
self.line(" db:seed - Seed database with test data")
435
self.line(" db:reset - Reset database")
436
437
return 0
438
439
class MigrateCommand(Command):
440
name = "db:migrate"
441
description = "Run database migrations"
442
443
def handle(self):
444
self.line("Running migrations...")
445
# Migration logic here
446
self.line("<info>Migrations completed</info>")
447
return 0
448
```
449
450
### Command Calling Other Commands
451
452
```python
453
class DeployCommand(Command):
454
name = "deploy"
455
description = "Deploy the application"
456
457
def handle(self):
458
self.line("Starting deployment...")
459
460
# Run tests first
461
if self.call("test") != 0:
462
self.line("<error>Tests failed, aborting deployment</error>")
463
return 1
464
465
# Build the application
466
self.call("build", "--production")
467
468
# Deploy silently
469
result = self.call_silent("upload", "--target production")
470
471
if result == 0:
472
self.line("<info>Deployment successful!</info>")
473
else:
474
self.line("<error>Deployment failed</error>")
475
476
return result
477
```