hexastack-cli 0.8.0__tar.gz → 0.10.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.
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/PKG-INFO +60 -3
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/README.md +59 -2
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/pyproject.toml +2 -2
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/pyproject.toml.orig +2 -2
- hexastack_cli-0.10.0/src/hexastack_cli/adapters/__init__.py +25 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/adapters/routing.py +6 -2
- hexastack_cli-0.10.0/src/hexastack_cli/adapters/suggest.py +417 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/app.py +2 -1
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/autodiscovery.py +35 -11
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/decorators.py +15 -2
- hexastack_cli-0.10.0/src/hexastack_cli/testing/narrator.py +323 -0
- hexastack_cli-0.8.0/src/hexastack_cli/adapters/__init__.py +0 -11
- hexastack_cli-0.8.0/src/hexastack_cli/testing/narrator.py +0 -188
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/__init__.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/adapters/presenter.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/domain/__init__.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/domain/config.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/domain/options.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/__init__.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/bootstrap.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/config.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/infra/options.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/py.typed +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/testing/__init__.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/testing/events.py +0 -0
- {hexastack_cli-0.8.0 → hexastack_cli-0.10.0}/src/hexastack_cli/testing/terminal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexastack-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Hexastack CLI presentation adapter with Typer and Rich
|
|
5
5
|
Author: Richard West
|
|
6
6
|
Author-email: Richard West <dopplereffect.us@gmail.com>
|
|
@@ -37,9 +37,11 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
- **Nested Command Hierarchies**: Nest subcommands naturally (`app user create`, `app db migrate`) using `@cli_group`.
|
|
39
39
|
- **Command Aliases**: Register multiple aliases for the same action (`app user new == app user create`).
|
|
40
|
+
- **Typo Tolerance & Suggestions (`TyperSuggest`)**: Drop-in replacement for `typer.Typer` that intercepts unrecognized subcommands or flags, computes closest matches via Levenshtein distance, prompts interactively for confirmation, and exits cleanly in CI/non-interactive pipelines.
|
|
40
41
|
- **Feature Flag Gating**: Gate CLI commands dynamically with `@feature_flag_command(...)` and `@cli_command(..., feature_flag=...)`.
|
|
41
42
|
- **Rich Formatted & CI-Friendly Output**: Beautiful tables, panels, and spinners for interactive terminals; clean text/JSON streaming for CI/CD pipelines.
|
|
42
43
|
- **Direct CQRS Dispatching**: Declaratively expose domain commands (`@cli_command`) and queries (`@cli_query`) with automatic parameter parsing and validation.
|
|
44
|
+
- **Automated Demo Narration & Slicing (`CliNarrator`)**: Headless terminal recording generating asciinema streams, high-resolution `.webm` videos, chapterized `.checkpoint_segment()` flushes, and synchronized `.vtt` subtitles.
|
|
43
45
|
|
|
44
46
|
---
|
|
45
47
|
|
|
@@ -48,7 +50,8 @@ Description-Content-Type: text/markdown
|
|
|
48
50
|
```
|
|
49
51
|
hexastack_cli/
|
|
50
52
|
├── domain/ # CliContext, OutputFormat enum
|
|
51
|
-
├── adapters/ # Rich presenters, Typer command runners
|
|
53
|
+
├── adapters/ # Rich presenters, Typer command runners, TyperSuggest
|
|
54
|
+
├── testing/ # CliNarrator, TerminalEvent (demo video & VTT recorder)
|
|
52
55
|
└── infra/ # create_cli_app, CliBootstrapper (order=40), @cli_command, @cli_query, @cli_group, @feature_flag_command
|
|
53
56
|
```
|
|
54
57
|
|
|
@@ -56,7 +59,7 @@ hexastack_cli/
|
|
|
56
59
|
|
|
57
60
|
| Category | Exports |
|
|
58
61
|
|---|---|
|
|
59
|
-
| **Application Factory** | `create_cli_app`, `CliBootstrapper` (order=
|
|
62
|
+
| **Application Factory & Typo Tolerance** | `create_cli_app`, `TyperSuggest`, `SuggestGroup`, `SuggestCommand`, `CliBootstrapper` (order=40) |
|
|
60
63
|
| **Decorators** | `@cli_command`, `@cli_query`, `@cli_group`, `@feature_flag_command` |
|
|
61
64
|
| **Presenters** | `RichTerminalPresenter`, `ConsolePresenter`, `TablePresenter`, `JsonPresenter` |
|
|
62
65
|
| **Testing & Demo Narration** | `CliNarrator`, `TerminalEvent` |
|
|
@@ -148,3 +151,57 @@ cli_query("status", aliases=["st", "health"], help="Check status of a service")(
|
|
|
148
151
|
runtime = bootstrap(packages_to_scan=[__name__])
|
|
149
152
|
cli_app = runtime.get("cli_app")
|
|
150
153
|
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 6. Standalone Typo-Tolerant CLI (`TyperSuggest`)
|
|
158
|
+
|
|
159
|
+
`TyperSuggest` is a drop-in replacement for `typer.Typer`. It intercepts mistyped commands and options, presenting suggestions and interactive confirmations without throwing cryptic syntax errors:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from hexastack_cli.adapters import TyperSuggest
|
|
163
|
+
|
|
164
|
+
app = TyperSuggest(help="My Resilient Service CLI")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@app.command()
|
|
168
|
+
def deploy(environment: str = "staging", force: bool = False):
|
|
169
|
+
print(f"Deploying to {environment} (force={force})")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
app()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
$ python cli.py deply
|
|
178
|
+
Unrecognized command 'deply'. Did you mean 'deploy'? [y/N]: y
|
|
179
|
+
Deploying to staging (force=False)
|
|
180
|
+
|
|
181
|
+
$ python cli.py deploy --frc
|
|
182
|
+
Unrecognized option '--frc'. Did you mean '--force'? [y/N]: y
|
|
183
|
+
Deploying to staging (force=True)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
In automated CI environments or redirected streams (`not sys.stdin.isatty()`), `TyperSuggest` prints the suggested correction to `stderr` and exits cleanly with error code `2` without hanging.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 7. Automated Demo Recording & Video Slicing (`CliNarrator`)
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from hexastack_cli.testing import CliNarrator
|
|
194
|
+
|
|
195
|
+
narrator = CliNarrator(record_video=True)
|
|
196
|
+
narrator.say("Scaffolding microservice...")
|
|
197
|
+
narrator.run("hexastack new web-api billing-service")
|
|
198
|
+
|
|
199
|
+
# Flush Chapter 1 video & VTT segment
|
|
200
|
+
narrator.checkpoint_segment("ch01-scaffolding")
|
|
201
|
+
|
|
202
|
+
narrator.say("Applying SQLite database migrations...")
|
|
203
|
+
narrator.run("hexastack db migrate")
|
|
204
|
+
|
|
205
|
+
# Flush Chapter 2 video & VTT segment
|
|
206
|
+
narrator.checkpoint_segment("ch02-migrations")
|
|
207
|
+
```
|
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
|
|
21
21
|
- **Nested Command Hierarchies**: Nest subcommands naturally (`app user create`, `app db migrate`) using `@cli_group`.
|
|
22
22
|
- **Command Aliases**: Register multiple aliases for the same action (`app user new == app user create`).
|
|
23
|
+
- **Typo Tolerance & Suggestions (`TyperSuggest`)**: Drop-in replacement for `typer.Typer` that intercepts unrecognized subcommands or flags, computes closest matches via Levenshtein distance, prompts interactively for confirmation, and exits cleanly in CI/non-interactive pipelines.
|
|
23
24
|
- **Feature Flag Gating**: Gate CLI commands dynamically with `@feature_flag_command(...)` and `@cli_command(..., feature_flag=...)`.
|
|
24
25
|
- **Rich Formatted & CI-Friendly Output**: Beautiful tables, panels, and spinners for interactive terminals; clean text/JSON streaming for CI/CD pipelines.
|
|
25
26
|
- **Direct CQRS Dispatching**: Declaratively expose domain commands (`@cli_command`) and queries (`@cli_query`) with automatic parameter parsing and validation.
|
|
27
|
+
- **Automated Demo Narration & Slicing (`CliNarrator`)**: Headless terminal recording generating asciinema streams, high-resolution `.webm` videos, chapterized `.checkpoint_segment()` flushes, and synchronized `.vtt` subtitles.
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
|
@@ -31,7 +33,8 @@
|
|
|
31
33
|
```
|
|
32
34
|
hexastack_cli/
|
|
33
35
|
├── domain/ # CliContext, OutputFormat enum
|
|
34
|
-
├── adapters/ # Rich presenters, Typer command runners
|
|
36
|
+
├── adapters/ # Rich presenters, Typer command runners, TyperSuggest
|
|
37
|
+
├── testing/ # CliNarrator, TerminalEvent (demo video & VTT recorder)
|
|
35
38
|
└── infra/ # create_cli_app, CliBootstrapper (order=40), @cli_command, @cli_query, @cli_group, @feature_flag_command
|
|
36
39
|
```
|
|
37
40
|
|
|
@@ -39,7 +42,7 @@ hexastack_cli/
|
|
|
39
42
|
|
|
40
43
|
| Category | Exports |
|
|
41
44
|
|---|---|
|
|
42
|
-
| **Application Factory** | `create_cli_app`, `CliBootstrapper` (order=
|
|
45
|
+
| **Application Factory & Typo Tolerance** | `create_cli_app`, `TyperSuggest`, `SuggestGroup`, `SuggestCommand`, `CliBootstrapper` (order=40) |
|
|
43
46
|
| **Decorators** | `@cli_command`, `@cli_query`, `@cli_group`, `@feature_flag_command` |
|
|
44
47
|
| **Presenters** | `RichTerminalPresenter`, `ConsolePresenter`, `TablePresenter`, `JsonPresenter` |
|
|
45
48
|
| **Testing & Demo Narration** | `CliNarrator`, `TerminalEvent` |
|
|
@@ -131,3 +134,57 @@ cli_query("status", aliases=["st", "health"], help="Check status of a service")(
|
|
|
131
134
|
runtime = bootstrap(packages_to_scan=[__name__])
|
|
132
135
|
cli_app = runtime.get("cli_app")
|
|
133
136
|
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 6. Standalone Typo-Tolerant CLI (`TyperSuggest`)
|
|
141
|
+
|
|
142
|
+
`TyperSuggest` is a drop-in replacement for `typer.Typer`. It intercepts mistyped commands and options, presenting suggestions and interactive confirmations without throwing cryptic syntax errors:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from hexastack_cli.adapters import TyperSuggest
|
|
146
|
+
|
|
147
|
+
app = TyperSuggest(help="My Resilient Service CLI")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@app.command()
|
|
151
|
+
def deploy(environment: str = "staging", force: bool = False):
|
|
152
|
+
print(f"Deploying to {environment} (force={force})")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
app()
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
$ python cli.py deply
|
|
161
|
+
Unrecognized command 'deply'. Did you mean 'deploy'? [y/N]: y
|
|
162
|
+
Deploying to staging (force=False)
|
|
163
|
+
|
|
164
|
+
$ python cli.py deploy --frc
|
|
165
|
+
Unrecognized option '--frc'. Did you mean '--force'? [y/N]: y
|
|
166
|
+
Deploying to staging (force=True)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
In automated CI environments or redirected streams (`not sys.stdin.isatty()`), `TyperSuggest` prints the suggested correction to `stderr` and exits cleanly with error code `2` without hanging.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 7. Automated Demo Recording & Video Slicing (`CliNarrator`)
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from hexastack_cli.testing import CliNarrator
|
|
177
|
+
|
|
178
|
+
narrator = CliNarrator(record_video=True)
|
|
179
|
+
narrator.say("Scaffolding microservice...")
|
|
180
|
+
narrator.run("hexastack new web-api billing-service")
|
|
181
|
+
|
|
182
|
+
# Flush Chapter 1 video & VTT segment
|
|
183
|
+
narrator.checkpoint_segment("ch01-scaffolding")
|
|
184
|
+
|
|
185
|
+
narrator.say("Applying SQLite database migrations...")
|
|
186
|
+
narrator.run("hexastack db migrate")
|
|
187
|
+
|
|
188
|
+
# Flush Chapter 2 video & VTT segment
|
|
189
|
+
narrator.checkpoint_segment("ch02-migrations")
|
|
190
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hexastack-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.0"
|
|
4
4
|
description = "Hexastack CLI presentation adapter with Typer and Rich"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -24,7 +24,7 @@ testing = ["playwright>=1.49.0"]
|
|
|
24
24
|
cli = "hexastack_cli.infra.bootstrap:CliBootstrapper"
|
|
25
25
|
|
|
26
26
|
[build-system]
|
|
27
|
-
requires = ["uv_build>=0.12.3,<0.
|
|
27
|
+
requires = ["uv_build>=0.12.3,<0.14.0"]
|
|
28
28
|
build-backend = "uv_build"
|
|
29
29
|
|
|
30
30
|
[tool.uv.sources.hexastack-core]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hexastack-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.0"
|
|
4
4
|
description = "Hexastack CLI presentation adapter with Typer and Rich"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -25,7 +25,7 @@ testing = [
|
|
|
25
25
|
cli = "hexastack_cli.infra.bootstrap:CliBootstrapper"
|
|
26
26
|
|
|
27
27
|
[build-system]
|
|
28
|
-
requires = ["uv_build>=0.12.3,<0.
|
|
28
|
+
requires = ["uv_build>=0.12.3,<0.14.0"]
|
|
29
29
|
build-backend = "uv_build"
|
|
30
30
|
|
|
31
31
|
[tool.uv.sources]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from hexastack_cli.adapters.presenter import RichTerminalPresenter
|
|
2
|
+
from hexastack_cli.adapters.routing import (
|
|
3
|
+
register_cqrs_command,
|
|
4
|
+
register_cqrs_query,
|
|
5
|
+
)
|
|
6
|
+
from hexastack_cli.adapters.suggest import (
|
|
7
|
+
SuggestCommand,
|
|
8
|
+
SuggestGroup,
|
|
9
|
+
SuggestOptionParser,
|
|
10
|
+
TyperSuggest,
|
|
11
|
+
find_closest_match,
|
|
12
|
+
is_interactive,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"find_closest_match",
|
|
17
|
+
"is_interactive",
|
|
18
|
+
"register_cqrs_command",
|
|
19
|
+
"register_cqrs_query",
|
|
20
|
+
"RichTerminalPresenter",
|
|
21
|
+
"SuggestCommand",
|
|
22
|
+
"SuggestGroup",
|
|
23
|
+
"SuggestOptionParser",
|
|
24
|
+
"TyperSuggest",
|
|
25
|
+
]
|
|
@@ -307,6 +307,7 @@ def register_cqrs_command(
|
|
|
307
307
|
presenter: RichTerminalPresenter | None = None,
|
|
308
308
|
console: Console | None = None,
|
|
309
309
|
feature_flag: str | None = None,
|
|
310
|
+
hidden: bool = False,
|
|
310
311
|
) -> None:
|
|
311
312
|
"""Register a CQRS Command model as a typed CLI sub-command on a Typer application.
|
|
312
313
|
|
|
@@ -325,6 +326,7 @@ def register_cqrs_command(
|
|
|
325
326
|
presenter: Optional RichTerminalPresenter instance.
|
|
326
327
|
console: Optional rich Console instance.
|
|
327
328
|
feature_flag: Optional feature flag key required for execution.
|
|
329
|
+
hidden: Whether to hide the command from CLI help documentation.
|
|
328
330
|
|
|
329
331
|
Returns:
|
|
330
332
|
None.
|
|
@@ -343,7 +345,7 @@ def register_cqrs_command(
|
|
|
343
345
|
console=console,
|
|
344
346
|
feature_flag=feature_flag,
|
|
345
347
|
)
|
|
346
|
-
app.command(name=cmd_name, help=doc)(runner)
|
|
348
|
+
app.command(name=cmd_name, help=doc, hidden=hidden)(runner)
|
|
347
349
|
|
|
348
350
|
|
|
349
351
|
def register_cqrs_query(
|
|
@@ -357,6 +359,7 @@ def register_cqrs_query(
|
|
|
357
359
|
presenter: RichTerminalPresenter | None = None,
|
|
358
360
|
console: Console | None = None,
|
|
359
361
|
feature_flag: str | None = None,
|
|
362
|
+
hidden: bool = False,
|
|
360
363
|
) -> None:
|
|
361
364
|
"""Register a CQRS Query model as a typed CLI sub-command on a Typer application.
|
|
362
365
|
|
|
@@ -375,6 +378,7 @@ def register_cqrs_query(
|
|
|
375
378
|
presenter: Optional RichTerminalPresenter instance.
|
|
376
379
|
console: Optional rich Console instance.
|
|
377
380
|
feature_flag: Optional feature flag key required for execution.
|
|
381
|
+
hidden: Whether to hide the command from CLI help documentation.
|
|
378
382
|
|
|
379
383
|
Returns:
|
|
380
384
|
None.
|
|
@@ -393,4 +397,4 @@ def register_cqrs_query(
|
|
|
393
397
|
console=console,
|
|
394
398
|
feature_flag=feature_flag,
|
|
395
399
|
)
|
|
396
|
-
app.command(name=qry_name, help=doc)(runner)
|
|
400
|
+
app.command(name=qry_name, help=doc, hidden=hidden)(runner)
|