clicycle 0.1.0__tar.gz

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,54 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.so
5
+
6
+ # Virtual environments
7
+ .venv/
8
+ venv/
9
+
10
+ # Build artifacts
11
+ build/
12
+ dist/
13
+ *.egg-info/
14
+
15
+ # Testing
16
+ htmlcov/
17
+ .coverage
18
+ .coverage.*
19
+ coverage.xml
20
+ coverage.json
21
+ .pytest_cache/
22
+ .tox/
23
+ .nox/
24
+
25
+ # Tools
26
+ .ruff_cache/
27
+ .mypy_cache/
28
+
29
+ # IDE
30
+ .vscode/
31
+ .idea/
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+
37
+ # Project specific
38
+ uv.lock
39
+ .pdm-python
40
+ .python-version
41
+
42
+ # Logs
43
+ *.log
44
+
45
+ # Documentation
46
+ docs/_build/
47
+ site/
48
+
49
+ # Temporary files
50
+ *.tmp
51
+ *.bak
52
+ *~
53
+ .*.swp
54
+ .*.swo
clicycle-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Living Content
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,631 @@
1
+ Metadata-Version: 2.4
2
+ Name: clicycle
3
+ Version: 0.1.0
4
+ Summary: HTML-like CLI framework with self-spacing components and Rich theming
5
+ Project-URL: Homepage, https://github.com/Living-Content/clicycle
6
+ Project-URL: Repository, https://github.com/Living-Content/clicycle.git
7
+ Project-URL: Issues, https://github.com/Living-Content/clicycle/issues
8
+ Project-URL: Documentation, https://github.com/Living-Content/clicycle#readme
9
+ Author-email: Living Content <hello@livingcontent.co>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,components,rich,terminal,ui
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Shells
25
+ Classifier: Topic :: Terminals
26
+ Requires-Python: >=3.8
27
+ Requires-Dist: click>=8.2.1
28
+ Requires-Dist: rich>=13.9.4
29
+ Provides-Extra: dev
30
+ Requires-Dist: mypy>=1.17.0; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.4.1; extra == 'dev'
33
+ Requires-Dist: ruff>=0.12.4; extra == 'dev'
34
+ Requires-Dist: types-click>=7.1.8; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # Clicycle
38
+
39
+ > HTML-like CLI framework with self-spacing components and Rich theming
40
+
41
+ **Clicycle** is a modern Python CLI framework that provides React/HTML-like components for building beautiful command-line interfaces with automatic spacing and Rich theming.
42
+
43
+ ## Features
44
+
45
+ - **Rich theming** - Comprehensive styling with icons, typography, and layout controls
46
+ - **Automatic spacing** - Components manage their own spacing like HTML elements
47
+ - **Component-based** - Familiar React/HTML-like API with composable components
48
+ - **Type-safe** - Full type hints and IDE support
49
+ - **Smart tables** - Automatically sized columns with intelligent formatting
50
+ - **Code highlighting** - Syntax-highlighted code blocks with line numbers
51
+ - **Progress tracking** - Built-in progress bars and spinners
52
+ - **Interactive prompts** - Properly spaced input prompts and confirmations
53
+
54
+ ## Installation
55
+
56
+ ### Using uv (Recommended)
57
+
58
+ [uv](https://github.com/astral-sh/uv) is a fast Python package installer and virtual environment manager.
59
+
60
+ ```bash
61
+ # Install clicycle
62
+ uv add clicycle
63
+
64
+ # Or install globally
65
+ uv tool install clicycle
66
+ ```
67
+
68
+ ### Using pip (not recommended)
69
+
70
+ ```bash
71
+ pip install clicycle
72
+ ```
73
+
74
+ ### Development Installation
75
+
76
+ ```bash
77
+ git clone https://github.com/yourusername/clicycle.git
78
+ cd clicycle
79
+
80
+ # Using uv (recommended)
81
+ uv venv
82
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
83
+ uv pip install -e ".[dev]"
84
+
85
+ # Using pip
86
+ pip install -e ".[dev]"
87
+ ```
88
+
89
+ ## Quick Start
90
+
91
+ ```python
92
+ from clicycle import Clicycle
93
+
94
+ # Create a CLI instance
95
+ cli = Clicycle(app_name="MyApp")
96
+
97
+ # Display a header
98
+ cli.header("Welcome", "Getting started with Clicycle")
99
+
100
+ # Show different message types
101
+ cli.info("This is an info message")
102
+ cli.success("Operation completed successfully!")
103
+ cli.warning("This is a warning")
104
+ cli.error("Something went wrong")
105
+
106
+ # Display a table
107
+ data = [
108
+ {"Name": "Alice", "Age": 30, "City": "New York"},
109
+ {"Name": "Bob", "Age": 25, "City": "San Francisco"},
110
+ {"Name": "Charlie", "Age": 35, "City": "Chicago"},
111
+ ]
112
+ cli.table(data, title="User Information")
113
+
114
+ # Show code with syntax highlighting
115
+ code = '''
116
+ def hello_world():
117
+ print("Hello, Clicycle!")
118
+ '''
119
+ cli.code(code, language="python", title="Example Code")
120
+
121
+ # Interactive prompts
122
+ name = cli.prompt("What's your name?")
123
+ confirmed = cli.confirm("Do you want to continue?")
124
+ ```
125
+
126
+ ## Components
127
+
128
+ ### Headers and Sections
129
+
130
+ ```python
131
+ from clicycle import Clicycle
132
+
133
+ cli = Clicycle()
134
+
135
+ # Main header with optional app branding
136
+ cli.header("Main Title", "Optional subtitle", app_name="MyApp")
137
+
138
+ # Section dividers
139
+ cli.section("Configuration")
140
+ ```
141
+
142
+ ### Text Messages
143
+
144
+ ```python
145
+ # Different message types with automatic icons
146
+ cli.info("Information message")
147
+ cli.success("Success message")
148
+ cli.warning("Warning message")
149
+ cli.error("Error message")
150
+ cli.debug("Debug message") # Only shown in verbose mode
151
+
152
+ # List items
153
+ cli.list("First item")
154
+ cli.list("Second item")
155
+ ```
156
+
157
+ ### Tables
158
+
159
+ ```python
160
+ # Simple table
161
+ data = [
162
+ {"Name": "Alice", "Score": 95},
163
+ {"Name": "Bob", "Score": 87},
164
+ ]
165
+ cli.table(data)
166
+
167
+ # Table with title
168
+ cli.table(data, title="Test Results")
169
+ ```
170
+
171
+ ### Code Display
172
+
173
+ ```python
174
+ # Python code with line numbers
175
+ cli.code('''
176
+ def fibonacci(n):
177
+ if n <= 1:
178
+ return n
179
+ return fibonacci(n-1) + fibonacci(n-2)
180
+ ''', language="python", title="Fibonacci Function")
181
+
182
+ # JSON data
183
+ cli.json({"name": "Alice", "age": 30}, title="User Data")
184
+ ```
185
+
186
+ ### Progress and Spinners
187
+
188
+ ```python
189
+ # Simple spinner
190
+ with cli.spinner("Loading data..."):
191
+ time.sleep(2)
192
+
193
+ # Progress bar
194
+ with cli.progress("Processing files") as progress_cli:
195
+ for i in range(100):
196
+ progress_cli.update_progress(i, f"Processing file {i}")
197
+ time.sleep(0.1)
198
+
199
+ # Multi-task progress
200
+ with cli.multi_progress("Multiple tasks") as progress:
201
+ task1 = progress.add_task("Task 1", total=100, short_id="T1")
202
+ task2 = progress.add_task("Task 2", total=50, short_id="T2")
203
+
204
+ for i in range(100):
205
+ progress.update(task1, advance=1)
206
+ if i % 2 == 0:
207
+ progress.update(task2, advance=1)
208
+ time.sleep(0.1)
209
+ ```
210
+
211
+ ### Interactive Prompts
212
+
213
+ ```python
214
+ # Basic prompts
215
+ name = cli.prompt("Enter your name")
216
+ age = cli.prompt("Enter your age", type=int)
217
+ confirmed = cli.confirm("Are you sure?")
218
+
219
+ # Selection from list
220
+ from clicycle import select_from_list
221
+
222
+ option = select_from_list(
223
+ "environment",
224
+ ["development", "staging", "production"],
225
+ default="development",
226
+ cli=cli
227
+ )
228
+ ```
229
+
230
+ ### Grouping with Blocks
231
+
232
+ ```python
233
+ # Group related content
234
+ cli.section("User Information")
235
+ with cli.block():
236
+ cli.info("Processing user data...")
237
+ cli.success("User validated")
238
+ cli.info("Creating profile...")
239
+ ```
240
+
241
+ ### Summary Data
242
+
243
+ ```python
244
+ # Key-value summaries
245
+ summary_data = [
246
+ {"label": "Total Files", "value": 1250},
247
+ {"label": "Processed", "value": 1200},
248
+ {"label": "Errors", "value": 50},
249
+ {"label": "Success Rate", "value": "96%"},
250
+ ]
251
+ cli.summary(summary_data)
252
+ ```
253
+
254
+ ## Theming
255
+
256
+ Clicycle provides comprehensive theming capabilities to match your application's branding and style preferences.
257
+
258
+ ### Basic Theming
259
+
260
+ ```python
261
+ from clicycle import Clicycle, Theme, Icons, Typography
262
+
263
+ # Create custom theme
264
+ custom_theme = Theme(
265
+ icons=Icons(
266
+ success="✅",
267
+ error="❌",
268
+ info="💡",
269
+ warning="⚠️",
270
+ debug="🔍",
271
+ bullet="▶"
272
+ ),
273
+ typography=Typography(
274
+ header_style="bold magenta",
275
+ success_style="bold green",
276
+ error_style="bold red on white",
277
+ info_style="bold blue",
278
+ warning_style="bold yellow"
279
+ )
280
+ )
281
+
282
+ cli = Clicycle(theme=custom_theme)
283
+ ```
284
+
285
+ ### Complete Theme Customization
286
+
287
+ ```python
288
+ from clicycle import Clicycle, Theme, Icons, Typography, Layout, ComponentSpacing
289
+
290
+ # Create a comprehensive custom theme
291
+ professional_theme = Theme(
292
+ icons=Icons(
293
+ success="✓",
294
+ error="✗",
295
+ info="ℹ",
296
+ warning="⚠",
297
+ debug="→",
298
+ bullet="•"
299
+ ),
300
+ typography=Typography(
301
+ # Headers and sections
302
+ header_style="bold white on blue",
303
+ section_style="bold cyan underline",
304
+
305
+ # Message styles
306
+ success_style="bold green",
307
+ error_style="bold red",
308
+ info_style="cyan",
309
+ warning_style="bold yellow",
310
+ debug_style="dim white",
311
+
312
+ # Table and code styles
313
+ table_header_style="bold white on black",
314
+ code_style="bright_black on white"
315
+ ),
316
+ layout=Layout(
317
+ table_style="rounded",
318
+ table_header_style="bold magenta",
319
+ table_row_styles=["none", "dim"],
320
+ code_theme="github-dark"
321
+ ),
322
+ spacing=ComponentSpacing(
323
+ after_header=2,
324
+ after_section=1,
325
+ between_components=1,
326
+ before_table=1,
327
+ after_table=1,
328
+ before_code=1,
329
+ after_code=1
330
+ )
331
+ )
332
+
333
+ # Use the professional theme
334
+ cli = Clicycle(theme=professional_theme)
335
+
336
+ # Demonstrate the themed output
337
+ cli.header("Professional Application", "With custom styling")
338
+ cli.section("System Status")
339
+ cli.success("All systems operational")
340
+ cli.info("Current version: 2.1.0")
341
+ cli.warning("Maintenance scheduled for tonight")
342
+
343
+ # Table with professional styling
344
+ data = [
345
+ {"Service": "API", "Status": "✓ Running", "Uptime": "99.9%"},
346
+ {"Service": "Database", "Status": "✓ Running", "Uptime": "99.8%"},
347
+ {"Service": "Cache", "Status": "⚠ Warning", "Uptime": "98.5%"}
348
+ ]
349
+ cli.table(data, title="Service Health")
350
+ ```
351
+
352
+ ### Theme Components
353
+
354
+ - **Icons** - All symbols and indicators (success, error, info, warning, debug, bullet)
355
+ - **Typography** - Text styles for every component type
356
+ - **Layout** - Table styling, code themes, and visual presentation
357
+ - **ComponentSpacing** - Precise control over spacing between all component types
358
+
359
+ ### Predefined Themes
360
+
361
+ ```python
362
+ # Minimal theme for clean output
363
+ minimal_theme = Theme(
364
+ icons=Icons(success="", error="", info="", warning="", debug="", bullet="·"),
365
+ typography=Typography(
366
+ header_style="bold",
367
+ success_style="green",
368
+ error_style="red",
369
+ info_style="blue",
370
+ warning_style="yellow"
371
+ )
372
+ )
373
+
374
+ # Colorful theme for engaging interfaces
375
+ colorful_theme = Theme(
376
+ icons=Icons(
377
+ success="🎉",
378
+ error="💥",
379
+ info="💬",
380
+ warning="🚨",
381
+ debug="🔧",
382
+ bullet="🔸"
383
+ ),
384
+ typography=Typography(
385
+ header_style="bold magenta on black",
386
+ success_style="bold green on black",
387
+ error_style="bold red on yellow",
388
+ info_style="bold blue on white",
389
+ warning_style="bold black on yellow"
390
+ )
391
+ )
392
+
393
+ cli = Clicycle(theme=minimal_theme) # or colorful_theme
394
+ ```
395
+
396
+ ## Advanced Usage
397
+
398
+ ### Custom CLI Instance
399
+
400
+ ```python
401
+ from clicycle import Clicycle, Theme
402
+
403
+ # Create CLI with custom settings
404
+ cli = Clicycle(
405
+ width=120, # Terminal width
406
+ app_name="MyApp", # App branding in headers
407
+ theme=custom_theme # Custom theme
408
+ )
409
+ ```
410
+
411
+ ### Integration with Click
412
+
413
+ Clicycle works seamlessly with Click for command-line applications:
414
+
415
+ ```python
416
+ import click
417
+ from clicycle import Clicycle
418
+
419
+ @click.command()
420
+ @click.option('--verbose', '-v', is_flag=True)
421
+ def main(verbose):
422
+ cli = Clicycle()
423
+
424
+ if verbose:
425
+ cli.debug("Verbose mode enabled")
426
+
427
+ cli.header("My Application", "Version 1.0")
428
+ cli.info("Application started")
429
+
430
+ if __name__ == '__main__':
431
+ main()
432
+ ```
433
+
434
+ ### Error Handling
435
+
436
+ ```python
437
+ try:
438
+ # Some operation
439
+ result = risky_operation()
440
+ cli.success(f"Operation completed: {result}")
441
+ except Exception as e:
442
+ cli.error(f"Operation failed: {e}")
443
+ cli.suggestions([
444
+ "Check your input parameters",
445
+ "Verify network connectivity",
446
+ "Try again with --verbose flag"
447
+ ])
448
+ ```
449
+
450
+ ## Performance
451
+
452
+ Clicycle is optimized for CLI applications with minimal overhead while providing rich functionality. Here's how to benchmark performance in your applications:
453
+
454
+ ### Performance Testing
455
+
456
+ ```python
457
+ #!/usr/bin/env python3
458
+ """Simple performance benchmark for clicycle."""
459
+
460
+ import io
461
+ import time
462
+ from rich.console import Console
463
+ from clicycle import Clicycle
464
+
465
+ def benchmark_clicycle():
466
+ """Benchmark clicycle performance against alternatives."""
467
+
468
+ def measure_time(func):
469
+ """Simple timing decorator."""
470
+ start_time = time.perf_counter()
471
+ func()
472
+ end_time = time.perf_counter()
473
+ return (end_time - start_time) * 1000 # Convert to milliseconds
474
+
475
+ # Test clicycle basic output
476
+ def test_clicycle():
477
+ cli = Clicycle()
478
+ # Capture output to avoid terminal spam during benchmarking
479
+ cli.stream.console = Console(file=io.StringIO())
480
+
481
+ cli.header("Performance Test", "Basic Output")
482
+ cli.info("This is an info message")
483
+ cli.success("This is a success message")
484
+ cli.warning("This is a warning message")
485
+ cli.error("This is an error message")
486
+
487
+ # Test table rendering
488
+ data = [
489
+ {"Name": f"User_{i}", "Score": i * 10, "Status": "Active"}
490
+ for i in range(100)
491
+ ]
492
+ cli.table(data, title="Performance Test Data")
493
+
494
+ # Test raw Rich for comparison
495
+ def test_raw_rich():
496
+ console = Console(file=io.StringIO())
497
+ console.print("[bold blue]Performance Test[/bold blue]")
498
+ console.print("ℹ This is an info message")
499
+ console.print("✓ This is a success message")
500
+ console.print("⚠ This is a warning message")
501
+ console.print("✗ This is an error message")
502
+
503
+ # Run benchmarks
504
+ clicycle_time = measure_time(test_clicycle)
505
+ rich_time = measure_time(test_raw_rich)
506
+
507
+ # Results
508
+ cli = Clicycle()
509
+ cli.header("Performance Results")
510
+
511
+ results = [
512
+ {"Test": "Clicycle (full features)", "Time (ms)": f"{clicycle_time:.2f}"},
513
+ {"Test": "Raw Rich (basic)", "Time (ms)": f"{rich_time:.2f}"},
514
+ {"Test": "Overhead", "Time (ms)": f"{clicycle_time - rich_time:.2f}"}
515
+ ]
516
+ cli.table(results, title="Benchmark Comparison")
517
+
518
+ overhead_percent = ((clicycle_time / rich_time) - 1) * 100
519
+ cli.info(f"Clicycle overhead: {overhead_percent:.1f}% vs raw Rich")
520
+
521
+ if clicycle_time < 50: # Less than 50ms is excellent for CLI
522
+ cli.success("Performance is excellent for CLI applications")
523
+ elif clicycle_time < 100:
524
+ cli.info("Performance is good for most CLI use cases")
525
+ else:
526
+ cli.warning("Consider optimizing for high-frequency operations")
527
+
528
+ if __name__ == "__main__":
529
+ benchmark_clicycle()
530
+ ```
531
+
532
+ ### Performance Characteristics
533
+
534
+ Based on our benchmarks, Clicycle delivers:
535
+
536
+ - **Startup time**: ~0.07ms (virtually instant)
537
+ - **Basic output**: ~1-2ms for typical message combinations
538
+ - **Large tables**: ~100ms for 1000 rows (same as raw Rich)
539
+ - **Memory usage**: Minimal overhead (~0.1MB for typical usage)
540
+
541
+ ### Performance Tips
542
+
543
+ - **Reuse CLI instances** - Create once, use many times
544
+ - **Batch operations** - Use `with cli.block():` to group related output
545
+ - **Table pagination** - For large datasets, consider pagination or streaming
546
+ - **Caching themes** - Custom themes are automatically cached for performance
547
+
548
+ ### When to Optimize
549
+
550
+ Clicycle's performance is designed for human-readable CLI output where microsecond differences are imperceptible. Consider optimization only for:
551
+
552
+ - High-frequency logging (>1000 operations/second)
553
+ - Real-time data streaming interfaces
554
+ - Resource-constrained environments
555
+ - Batch processing of large datasets
556
+
557
+ For most CLI applications, Clicycle's rich features and automatic spacing provide excellent value with negligible performance impact.
558
+
559
+ ## API Reference
560
+
561
+ ### Clicycle Class
562
+
563
+ The main class for creating CLI interfaces.
564
+
565
+ #### Constructor
566
+
567
+ ```python
568
+ Clicycle(width: int = 100, theme: Theme | None = None, app_name: str | None = None)
569
+ ```
570
+
571
+ #### Methods
572
+
573
+ - `header(title: str, subtitle: str = None, app_name: str = None)` - Display header
574
+ - `section(title: str)` - Display section divider
575
+ - `info/success/warning/error/debug(message: str)` - Display styled messages
576
+ - `table(data: list[dict], title: str = None)` - Display data table
577
+ - `code(code: str, language: str = "python", title: str = None, line_numbers: bool = True)` - Display code
578
+ - `json(data: dict, title: str = None)` - Display JSON data
579
+ - `summary(data: list[dict])` - Display key-value summary
580
+ - `list(item: str)` - Display list item
581
+ - `prompt(text: str, **kwargs)` - Interactive prompt
582
+ - `confirm(text: str, **kwargs)` - Interactive confirmation
583
+ - `suggestions(suggestions: list[str])` - Display suggestion list
584
+ - `spinner(message: str)` - Context manager for spinner
585
+ - `progress(description: str)` - Context manager for progress bar
586
+ - `multi_progress(description: str)` - Context manager for multi-task progress
587
+ - `block()` - Context manager for grouping components
588
+ - `clear()` - Clear terminal and reset
589
+
590
+ ## Contributing
591
+
592
+ 1. Fork the repository
593
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
594
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
595
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
596
+ 5. Open a Pull Request
597
+
598
+ ## Development
599
+
600
+ ```bash
601
+ # Install development dependencies using uv (recommended)
602
+ uv pip install -e ".[dev]"
603
+
604
+ # Or using pip
605
+ pip install -e ".[dev]"
606
+
607
+ # Run tests
608
+ pytest
609
+
610
+ # Run linting
611
+ ruff check .
612
+ ruff format .
613
+
614
+ # Run type checking
615
+ mypy src/
616
+ ```
617
+
618
+ ## License
619
+
620
+ MIT License - see [LICENSE](LICENSE) file for details.
621
+
622
+ ## Changelog
623
+
624
+ ### 0.1.0
625
+
626
+ - Initial release
627
+ - Core component system
628
+ - Rich theming support
629
+ - Automatic spacing
630
+ - Progress tracking
631
+ - Interactive prompts