milo-cli 0.3.1__tar.gz → 0.4.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.
- {milo_cli-0.3.1 → milo_cli-0.4.0}/PKG-INFO +48 -28
- {milo_cli-0.3.1 → milo_cli-0.4.0}/README.md +47 -27
- {milo_cli-0.3.1 → milo_cli-0.4.0}/pyproject.toml +1 -1
- milo_cli-0.4.0/src/milo/__init__.py +320 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_cells.py +5 -0
- milo_cli-0.4.0/src/milo/_child.py +326 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_cli_help.py +15 -14
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_command_defs.py +59 -1
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_errors.py +16 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_jsonrpc.py +3 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_mcp_router.py +39 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_scaffold/default/README.md +18 -17
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_scaffold/default/app.py +3 -3
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_scaffold/default/tests/test_app.py +16 -3
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/cli.py +3 -3
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/commands.py +595 -144
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/context.py +89 -21
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/gateway.py +311 -33
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/groups.py +19 -2
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/llms.py +21 -5
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/mcp.py +271 -19
- milo_cli-0.4.0/src/milo/mcp_apps.py +152 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/schema.py +388 -1
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/__init__.py +2 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/components/_defs.kida +21 -18
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/testing/_mcp.py +9 -4
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/testing/_snapshot.py +22 -5
- milo_cli-0.4.0/src/milo/verify.py +1287 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo_cli.egg-info/PKG-INFO +48 -28
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo_cli.egg-info/SOURCES.txt +9 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_ai_native.py +44 -8
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_child.py +133 -26
- milo_cli-0.4.0/tests/test_chirp_adoption_contract.py +255 -0
- milo_cli-0.4.0/tests/test_command_contract.py +333 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_command_defs.py +10 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_commands_middleware.py +17 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_components.py +84 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_context.py +184 -1
- milo_cli-0.4.0/tests/test_ctxdemo_example.py +28 -0
- milo_cli-0.4.0/tests/test_docs_information_architecture.py +151 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_docs_snippets.py +16 -0
- milo_cli-0.4.0/tests/test_downloader_example.py +47 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_effects.py +28 -8
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_effects_stress.py +64 -17
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_gateway.py +39 -1
- milo_cli-0.4.0/tests/test_gateway_mcp_apps.py +360 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_groups.py +12 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_help.py +48 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_lazy.py +114 -0
- milo_cli-0.4.0/tests/test_mcp_app_example.py +164 -0
- milo_cli-0.4.0/tests/test_mcp_apps.py +472 -0
- milo_cli-0.4.0/tests/test_mcp_compat_docs.py +55 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_mcp_handler.py +65 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_mcp_router.py +15 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_mcp_transport.py +20 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_milo_init.py +5 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_readme_example_index.py +25 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_scaffold.py +26 -3
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_schema_v2.py +222 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_templates.py +5 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_testing.py +26 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_testing_mcp.py +46 -1
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_verify.py +14 -2
- milo_cli-0.4.0/tests/test_verify_mcp_apps.py +351 -0
- milo_cli-0.3.1/src/milo/__init__.py +0 -282
- milo_cli-0.3.1/src/milo/_child.py +0 -190
- milo_cli-0.3.1/src/milo/verify.py +0 -465
- milo_cli-0.3.1/tests/test_command_contract.py +0 -173
- milo_cli-0.3.1/tests/test_docs_information_architecture.py +0 -64
- {milo_cli-0.3.1 → milo_cli-0.4.0}/setup.cfg +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_compat.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_protocols.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_scaffold/__init__.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_scaffold/default/conftest.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_scaffold/default/tests/__init__.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/_types.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/app.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/completions.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/components_cli.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/config.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/dev.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/doctor.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/flow.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/form.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/help.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/input/__init__.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/input/_platform.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/input/_reader.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/input/_sequences.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/live.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/middleware.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/observability.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/output.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/pipeline.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/plugins.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/py.typed +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/reducers.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/registry.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/state.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/streaming.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/components/command_list.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/components/help_page.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/error.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/field_confirm.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/field_select.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/field_text.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/form.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/help.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/templates/progress.kida +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/testing/__init__.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/testing/_record.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/testing/_replay.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/theme.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo/version_check.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo_cli.egg-info/dependency_links.txt +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo_cli.egg-info/entry_points.txt +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo_cli.egg-info/requires.txt +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/src/milo_cli.egg-info/top_level.txt +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_app.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_bubbletea_patterns.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_cli.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_commands_core.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_compat.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_completions.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_config.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_dev.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_doctor.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_errors.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_flow.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_form.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_input.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_mcp_prompts.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_mcp_resources.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_middleware.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_migration_docs.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_mount.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_observability.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_outputgallery_example.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_pipeline.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_plugins.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_protocols.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_reducers.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_registry_crud.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_registry_v2.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_release_status.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_state.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_streaming.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_theme.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_types.py +0 -0
- {milo_cli-0.3.1 → milo_cli-0.4.0}/tests/test_version_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: milo-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Template-driven CLI applications for free-threaded Python
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://lbliii.github.io/milo-cli/
|
|
@@ -33,33 +33,43 @@ Requires-Dist: watchfiles>=1.0; extra == "watch"
|
|
|
33
33
|
[](https://pypi.org/project/milo-cli/)
|
|
34
34
|
[](https://opensource.org/licenses/MIT)
|
|
35
35
|
|
|
36
|
-
**
|
|
36
|
+
**One typed Python function becomes a human CLI command, an MCP tool, and an
|
|
37
|
+
agent-readable discovery entry.**
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
<!--
|
|
40
|
+
Launch video: replace this comment with the final 60–90 second recording.
|
|
41
|
+
The verified shot list and clean-machine commands live in docs/launch-demo-script.md.
|
|
42
|
+
-->
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
## Prove It in 60 Seconds
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return {"status": "deployed", "environment": environment, "service": service, "version": version}
|
|
46
|
+
With [`uv`](https://docs.astral.sh/uv/) installed, paste this into a clean
|
|
47
|
+
directory. `uv` downloads Python 3.14 and Milo when they are not already
|
|
48
|
+
available:
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
```bash milo-docs:skip reason=downloads-package-and-creates-project
|
|
51
|
+
uvx --python 3.14 --from milo-cli milo new hello_milo
|
|
52
|
+
uv run --python 3.14 --with milo-cli python hello_milo/app.py greet --name World
|
|
53
|
+
uv run --python 3.14 --with milo-cli milo verify hello_milo/app.py
|
|
49
54
|
```
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
The second command prints `Hello, World!`. The verifier then exercises import,
|
|
57
|
+
schema generation, MCP discovery, MCP Apps tool/resource/gateway conformance,
|
|
58
|
+
and a real subprocess JSON-RPC handshake with resource reads. Do not register a
|
|
59
|
+
new tool until it reports zero failures.
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
Now give Claude Code the same file as a local stdio MCP server:
|
|
62
|
+
|
|
63
|
+
```bash milo-docs:skip reason=requires-claude-cli-and-user-registration
|
|
64
|
+
claude mcp add --transport stdio hello_milo -- \
|
|
65
|
+
uv run --python 3.14 --with milo-cli python "$PWD/hello_milo/app.py" --mcp
|
|
66
|
+
```
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| deployer --mcp
|
|
68
|
+
Ask Claude to “use the `greet` tool to greet Ada,” or call the same function by
|
|
69
|
+
hand:
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
```bash milo-docs:skip reason=requires-prior-scaffold
|
|
72
|
+
uv run --python 3.14 --with milo-cli python hello_milo/app.py greet --name Ada
|
|
63
73
|
```
|
|
64
74
|
|
|
65
75
|
---
|
|
@@ -72,7 +82,7 @@ Milo is a Python framework where every CLI is simultaneously a terminal app, a c
|
|
|
72
82
|
|
|
73
83
|
- **Every CLI is an MCP server** — `@cli.command` produces an argparse subcommand, MCP tool, and llms.txt entry from one function. AI agents discover and call your tools with zero extra code.
|
|
74
84
|
- **Dual-mode commands** — The same command shows an interactive UI when a human runs it, and returns structured JSON when an AI calls it via MCP.
|
|
75
|
-
- **Annotated schemas** — Type hints + `Annotated` constraints generate rich JSON Schema
|
|
85
|
+
- **Annotated schemas** — Type hints + `Annotated` constraints generate rich JSON Schema, and Milo enforces it before handlers run.
|
|
76
86
|
- **Streaming progress** — Commands that yield `Progress` objects stream notifications to MCP clients in real time.
|
|
77
87
|
- **Elm Architecture** — Immutable state, pure reducers, declarative views. Every state transition is explicit and testable.
|
|
78
88
|
- **Free-threading ready** — Built for Python 3.14t (PEP 703). Sagas run on `ThreadPoolExecutor` with no GIL contention.
|
|
@@ -121,7 +131,12 @@ The PyPI package is **milo-cli**; import the **`milo`** namespace in Python. The
|
|
|
121
131
|
| `--llms-txt` | Generate AI discovery doc |
|
|
122
132
|
| `--mcp-install` | Register in gateway |
|
|
123
133
|
| `annotations={...}` | MCP behavioral hints |
|
|
134
|
+
| `ui=MCPAppToolMeta("ui://...")` | Link a tool to an MCP Apps UI resource |
|
|
124
135
|
| `Annotated[str, MinLen(1)]` | Schema constraints |
|
|
136
|
+
| `Annotated[str, Positional("NAME")]` | Positional CLI presentation |
|
|
137
|
+
| `Option(aliases=("-n",))` | Compatible option aliases |
|
|
138
|
+
| `surfaces=("cli",)` | Keep long-running commands out of agent discovery |
|
|
139
|
+
| `terminal_renderer=...` | Human output over structured command results |
|
|
125
140
|
|
|
126
141
|
### Interactive Apps
|
|
127
142
|
|
|
@@ -148,7 +163,7 @@ The PyPI package is **milo-cli**; import the **`milo`** namespace in Python. The
|
|
|
148
163
|
| **MCP Gateway** | Single gateway aggregates all registered Milo CLIs for unified AI agent access | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
149
164
|
| **Tool Annotations** | Declare `readOnlyHint`, `destructiveHint`, `idempotentHint` per MCP spec | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
150
165
|
| **Streaming Progress** | Commands yield `Progress` objects; MCP clients receive real-time notifications | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
151
|
-
| **Schema Constraints** | `Annotated[str, MinLen(1), MaxLen(100)]` generates rich JSON Schema | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
166
|
+
| **Schema Constraints** | `Annotated[str, MinLen(1), MaxLen(100)]` generates and enforces rich JSON Schema | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
152
167
|
| **llms.txt** | Generate AI-readable discovery documents from CLI command definitions | [llms.txt →](https://lbliii.github.io/milo-cli/docs/build-clis/llms/) |
|
|
153
168
|
| **Middleware** | Intercept MCP calls and CLI commands for logging, auth, and transformation | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
154
169
|
| **Observability** | Built-in request logging with latency stats (`milo://stats` resource) | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
@@ -165,7 +180,7 @@ The PyPI package is **milo-cli**; import the **`milo`** namespace in Python. The
|
|
|
165
180
|
| **Snapshot Testing** | `assert_renders`, `assert_state`, `assert_saga` for deterministic test coverage | [Testing →](https://lbliii.github.io/milo-cli/docs/quality/testing/) |
|
|
166
181
|
| **Pipeline** | Declarative multi-phase workflows with dependency graphs, retry policies, and output capture | [Pipeline →](https://lbliii.github.io/milo-cli/docs/quality/pipeline/) |
|
|
167
182
|
| **Help Rendering** | `HelpRenderer` — drop-in `argparse.HelpFormatter` using Kida templates | [Help →](https://lbliii.github.io/milo-cli/docs/build-clis/help/) |
|
|
168
|
-
| **Context** |
|
|
183
|
+
| **Context** | Injectable output, interaction, approvals, global options, and `run_app()` bridge | [Context →](https://lbliii.github.io/milo-cli/docs/build-clis/context/) |
|
|
169
184
|
| **Configuration** | `Config` with validation, init scaffolding, and profile support | [Config →](https://lbliii.github.io/milo-cli/docs/about/concepts/configuration/) |
|
|
170
185
|
| **Shell Completions** | Generate bash/zsh/fish completions from CLI definitions | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
171
186
|
| **Doctor Diagnostics** | `run_doctor()` validates environment, dependencies, and config health | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
@@ -182,11 +197,12 @@ Pick the example closest to your use case, copy its `app.py`, and adapt. See [ex
|
|
|
182
197
|
|---|---|---|
|
|
183
198
|
| The simplest possible CLI | [examples/greet](examples/greet) | `CLI`, `@cli.command` |
|
|
184
199
|
| Dual-mode CLI ↔ MCP server (flagship) | [examples/deploy](examples/deploy) | `Annotated`, `MinLen`, `Context`, `Progress`, `--mcp` |
|
|
185
|
-
|
|
|
200
|
+
| MCP tool with a negotiated interactive UI resource | [examples/mcp_app](examples/mcp_app) | Dependency-free HTML, `ui_resource`, `MCPAppToolMeta`, structured fallback |
|
|
201
|
+
| Context injection, host-owned output, progress, confirms | [examples/ctxdemo](examples/ctxdemo) | `Context`, `OutputSink`, `ctx.progress`, `ctx.confirm` |
|
|
186
202
|
| Nested command groups (`app repo list`) | [examples/groups](examples/groups) | `cli.group()`, `walk_commands` |
|
|
187
203
|
| Fast startup via deferred imports | [examples/lazyapp](examples/lazyapp) | `cli.lazy_command()` |
|
|
188
|
-
| Production CLI with hooks, completions, doctor | [examples/devtool](examples/devtool) | `run_doctor`, `
|
|
189
|
-
| AI-native CLI surfacing tools + resources | [examples/taskman](examples/taskman) | `@command`, `@resource`, `--format`, `--llms-txt`, `--mcp` |
|
|
204
|
+
| Production CLI with hooks, completions, doctor | [examples/devtool](examples/devtool) | `run_doctor`, `before_command`/`after_command`, did-you-mean, completions |
|
|
205
|
+
| AI-native CLI surfacing tools + resources | [examples/taskman](examples/taskman) | `@cli.command`, `@cli.resource`, `--format`, `--llms-txt`, `--mcp` |
|
|
190
206
|
| Advanced terminal reports and diagnostics | [examples/outputgallery](examples/outputgallery) | `Context.render`, Kida templates, character maps, JSON output |
|
|
191
207
|
|
|
192
208
|
**Configuration, plugins, pipelines**
|
|
@@ -267,7 +283,8 @@ Every Milo CLI is automatically an MCP server:
|
|
|
267
283
|
myapp --mcp
|
|
268
284
|
|
|
269
285
|
# Register with an AI host directly
|
|
270
|
-
claude mcp add
|
|
286
|
+
claude mcp add --transport stdio myapp -- \
|
|
287
|
+
uv run python "$PWD/examples/deploy/app.py" --mcp
|
|
271
288
|
```
|
|
272
289
|
|
|
273
290
|
For multiple CLIs, register them and run a single gateway:
|
|
@@ -281,10 +298,13 @@ deployer --mcp-install
|
|
|
281
298
|
uv run python -m milo.gateway --mcp
|
|
282
299
|
|
|
283
300
|
# Or register the gateway with your AI host
|
|
284
|
-
claude mcp add milo -- uv run python -m milo.gateway --mcp
|
|
301
|
+
claude mcp add --transport stdio milo -- uv run python -m milo.gateway --mcp
|
|
285
302
|
```
|
|
286
303
|
|
|
287
|
-
The gateway namespaces tools automatically
|
|
304
|
+
The gateway namespaces tools automatically (`taskman.add`, `deployer.deploy`) and
|
|
305
|
+
rewrites negotiated MCP Apps `ui://` links without collisions. It preserves
|
|
306
|
+
`outputSchema`, `structuredContent`, tool annotations, resource metadata, and
|
|
307
|
+
streaming `Progress` notifications across child CLIs.
|
|
288
308
|
|
|
289
309
|
Built-in `milo://stats` resource exposes request latency, error counts, and throughput.
|
|
290
310
|
|
|
@@ -5,33 +5,43 @@
|
|
|
5
5
|
[](https://pypi.org/project/milo-cli/)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
**
|
|
8
|
+
**One typed Python function becomes a human CLI command, an MCP tool, and an
|
|
9
|
+
agent-readable discovery entry.**
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
<!--
|
|
12
|
+
Launch video: replace this comment with the final 60–90 second recording.
|
|
13
|
+
The verified shot list and clean-machine commands live in docs/launch-demo-script.md.
|
|
14
|
+
-->
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Prove It in 60 Seconds
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return {"status": "deployed", "environment": environment, "service": service, "version": version}
|
|
18
|
+
With [`uv`](https://docs.astral.sh/uv/) installed, paste this into a clean
|
|
19
|
+
directory. `uv` downloads Python 3.14 and Milo when they are not already
|
|
20
|
+
available:
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
```bash milo-docs:skip reason=downloads-package-and-creates-project
|
|
23
|
+
uvx --python 3.14 --from milo-cli milo new hello_milo
|
|
24
|
+
uv run --python 3.14 --with milo-cli python hello_milo/app.py greet --name World
|
|
25
|
+
uv run --python 3.14 --with milo-cli milo verify hello_milo/app.py
|
|
21
26
|
```
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
The second command prints `Hello, World!`. The verifier then exercises import,
|
|
29
|
+
schema generation, MCP discovery, MCP Apps tool/resource/gateway conformance,
|
|
30
|
+
and a real subprocess JSON-RPC handshake with resource reads. Do not register a
|
|
31
|
+
new tool until it reports zero failures.
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
Now give Claude Code the same file as a local stdio MCP server:
|
|
34
|
+
|
|
35
|
+
```bash milo-docs:skip reason=requires-claude-cli-and-user-registration
|
|
36
|
+
claude mcp add --transport stdio hello_milo -- \
|
|
37
|
+
uv run --python 3.14 --with milo-cli python "$PWD/hello_milo/app.py" --mcp
|
|
38
|
+
```
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
| deployer --mcp
|
|
40
|
+
Ask Claude to “use the `greet` tool to greet Ada,” or call the same function by
|
|
41
|
+
hand:
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
```bash milo-docs:skip reason=requires-prior-scaffold
|
|
44
|
+
uv run --python 3.14 --with milo-cli python hello_milo/app.py greet --name Ada
|
|
35
45
|
```
|
|
36
46
|
|
|
37
47
|
---
|
|
@@ -44,7 +54,7 @@ Milo is a Python framework where every CLI is simultaneously a terminal app, a c
|
|
|
44
54
|
|
|
45
55
|
- **Every CLI is an MCP server** — `@cli.command` produces an argparse subcommand, MCP tool, and llms.txt entry from one function. AI agents discover and call your tools with zero extra code.
|
|
46
56
|
- **Dual-mode commands** — The same command shows an interactive UI when a human runs it, and returns structured JSON when an AI calls it via MCP.
|
|
47
|
-
- **Annotated schemas** — Type hints + `Annotated` constraints generate rich JSON Schema
|
|
57
|
+
- **Annotated schemas** — Type hints + `Annotated` constraints generate rich JSON Schema, and Milo enforces it before handlers run.
|
|
48
58
|
- **Streaming progress** — Commands that yield `Progress` objects stream notifications to MCP clients in real time.
|
|
49
59
|
- **Elm Architecture** — Immutable state, pure reducers, declarative views. Every state transition is explicit and testable.
|
|
50
60
|
- **Free-threading ready** — Built for Python 3.14t (PEP 703). Sagas run on `ThreadPoolExecutor` with no GIL contention.
|
|
@@ -93,7 +103,12 @@ The PyPI package is **milo-cli**; import the **`milo`** namespace in Python. The
|
|
|
93
103
|
| `--llms-txt` | Generate AI discovery doc |
|
|
94
104
|
| `--mcp-install` | Register in gateway |
|
|
95
105
|
| `annotations={...}` | MCP behavioral hints |
|
|
106
|
+
| `ui=MCPAppToolMeta("ui://...")` | Link a tool to an MCP Apps UI resource |
|
|
96
107
|
| `Annotated[str, MinLen(1)]` | Schema constraints |
|
|
108
|
+
| `Annotated[str, Positional("NAME")]` | Positional CLI presentation |
|
|
109
|
+
| `Option(aliases=("-n",))` | Compatible option aliases |
|
|
110
|
+
| `surfaces=("cli",)` | Keep long-running commands out of agent discovery |
|
|
111
|
+
| `terminal_renderer=...` | Human output over structured command results |
|
|
97
112
|
|
|
98
113
|
### Interactive Apps
|
|
99
114
|
|
|
@@ -120,7 +135,7 @@ The PyPI package is **milo-cli**; import the **`milo`** namespace in Python. The
|
|
|
120
135
|
| **MCP Gateway** | Single gateway aggregates all registered Milo CLIs for unified AI agent access | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
121
136
|
| **Tool Annotations** | Declare `readOnlyHint`, `destructiveHint`, `idempotentHint` per MCP spec | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
122
137
|
| **Streaming Progress** | Commands yield `Progress` objects; MCP clients receive real-time notifications | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
123
|
-
| **Schema Constraints** | `Annotated[str, MinLen(1), MaxLen(100)]` generates rich JSON Schema | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
138
|
+
| **Schema Constraints** | `Annotated[str, MinLen(1), MaxLen(100)]` generates and enforces rich JSON Schema | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
124
139
|
| **llms.txt** | Generate AI-readable discovery documents from CLI command definitions | [llms.txt →](https://lbliii.github.io/milo-cli/docs/build-clis/llms/) |
|
|
125
140
|
| **Middleware** | Intercept MCP calls and CLI commands for logging, auth, and transformation | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
126
141
|
| **Observability** | Built-in request logging with latency stats (`milo://stats` resource) | [MCP →](https://lbliii.github.io/milo-cli/docs/build-clis/mcp/) |
|
|
@@ -137,7 +152,7 @@ The PyPI package is **milo-cli**; import the **`milo`** namespace in Python. The
|
|
|
137
152
|
| **Snapshot Testing** | `assert_renders`, `assert_state`, `assert_saga` for deterministic test coverage | [Testing →](https://lbliii.github.io/milo-cli/docs/quality/testing/) |
|
|
138
153
|
| **Pipeline** | Declarative multi-phase workflows with dependency graphs, retry policies, and output capture | [Pipeline →](https://lbliii.github.io/milo-cli/docs/quality/pipeline/) |
|
|
139
154
|
| **Help Rendering** | `HelpRenderer` — drop-in `argparse.HelpFormatter` using Kida templates | [Help →](https://lbliii.github.io/milo-cli/docs/build-clis/help/) |
|
|
140
|
-
| **Context** |
|
|
155
|
+
| **Context** | Injectable output, interaction, approvals, global options, and `run_app()` bridge | [Context →](https://lbliii.github.io/milo-cli/docs/build-clis/context/) |
|
|
141
156
|
| **Configuration** | `Config` with validation, init scaffolding, and profile support | [Config →](https://lbliii.github.io/milo-cli/docs/about/concepts/configuration/) |
|
|
142
157
|
| **Shell Completions** | Generate bash/zsh/fish completions from CLI definitions | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
143
158
|
| **Doctor Diagnostics** | `run_doctor()` validates environment, dependencies, and config health | [CLI →](https://lbliii.github.io/milo-cli/docs/build-clis/commands/) |
|
|
@@ -154,11 +169,12 @@ Pick the example closest to your use case, copy its `app.py`, and adapt. See [ex
|
|
|
154
169
|
|---|---|---|
|
|
155
170
|
| The simplest possible CLI | [examples/greet](examples/greet) | `CLI`, `@cli.command` |
|
|
156
171
|
| Dual-mode CLI ↔ MCP server (flagship) | [examples/deploy](examples/deploy) | `Annotated`, `MinLen`, `Context`, `Progress`, `--mcp` |
|
|
157
|
-
|
|
|
172
|
+
| MCP tool with a negotiated interactive UI resource | [examples/mcp_app](examples/mcp_app) | Dependency-free HTML, `ui_resource`, `MCPAppToolMeta`, structured fallback |
|
|
173
|
+
| Context injection, host-owned output, progress, confirms | [examples/ctxdemo](examples/ctxdemo) | `Context`, `OutputSink`, `ctx.progress`, `ctx.confirm` |
|
|
158
174
|
| Nested command groups (`app repo list`) | [examples/groups](examples/groups) | `cli.group()`, `walk_commands` |
|
|
159
175
|
| Fast startup via deferred imports | [examples/lazyapp](examples/lazyapp) | `cli.lazy_command()` |
|
|
160
|
-
| Production CLI with hooks, completions, doctor | [examples/devtool](examples/devtool) | `run_doctor`, `
|
|
161
|
-
| AI-native CLI surfacing tools + resources | [examples/taskman](examples/taskman) | `@command`, `@resource`, `--format`, `--llms-txt`, `--mcp` |
|
|
176
|
+
| Production CLI with hooks, completions, doctor | [examples/devtool](examples/devtool) | `run_doctor`, `before_command`/`after_command`, did-you-mean, completions |
|
|
177
|
+
| AI-native CLI surfacing tools + resources | [examples/taskman](examples/taskman) | `@cli.command`, `@cli.resource`, `--format`, `--llms-txt`, `--mcp` |
|
|
162
178
|
| Advanced terminal reports and diagnostics | [examples/outputgallery](examples/outputgallery) | `Context.render`, Kida templates, character maps, JSON output |
|
|
163
179
|
|
|
164
180
|
**Configuration, plugins, pipelines**
|
|
@@ -239,7 +255,8 @@ Every Milo CLI is automatically an MCP server:
|
|
|
239
255
|
myapp --mcp
|
|
240
256
|
|
|
241
257
|
# Register with an AI host directly
|
|
242
|
-
claude mcp add
|
|
258
|
+
claude mcp add --transport stdio myapp -- \
|
|
259
|
+
uv run python "$PWD/examples/deploy/app.py" --mcp
|
|
243
260
|
```
|
|
244
261
|
|
|
245
262
|
For multiple CLIs, register them and run a single gateway:
|
|
@@ -253,10 +270,13 @@ deployer --mcp-install
|
|
|
253
270
|
uv run python -m milo.gateway --mcp
|
|
254
271
|
|
|
255
272
|
# Or register the gateway with your AI host
|
|
256
|
-
claude mcp add milo -- uv run python -m milo.gateway --mcp
|
|
273
|
+
claude mcp add --transport stdio milo -- uv run python -m milo.gateway --mcp
|
|
257
274
|
```
|
|
258
275
|
|
|
259
|
-
The gateway namespaces tools automatically
|
|
276
|
+
The gateway namespaces tools automatically (`taskman.add`, `deployer.deploy`) and
|
|
277
|
+
rewrites negotiated MCP Apps `ui://` links without collisions. It preserves
|
|
278
|
+
`outputSchema`, `structuredContent`, tool annotations, resource metadata, and
|
|
279
|
+
streaming `Progress` notifications across child CLIs.
|
|
260
280
|
|
|
261
281
|
Built-in `milo://stats` resource exposes request latency, error counts, and throughput.
|
|
262
282
|
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""Milo — Template-driven CLI applications for free-threaded Python."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
_LAZY_IMPORTS = {
|
|
6
|
+
# Types
|
|
7
|
+
"Action": "_types",
|
|
8
|
+
"Key": "_types",
|
|
9
|
+
"SpecialKey": "_types",
|
|
10
|
+
"AppStatus": "_types",
|
|
11
|
+
"RenderTarget": "_types",
|
|
12
|
+
"FieldType": "_types",
|
|
13
|
+
"FieldSpec": "_types",
|
|
14
|
+
"FieldState": "_types",
|
|
15
|
+
"FormState": "_types",
|
|
16
|
+
"Screen": "_types",
|
|
17
|
+
"Transition": "_types",
|
|
18
|
+
"ReducerResult": "_types",
|
|
19
|
+
"Quit": "_types",
|
|
20
|
+
"Call": "_types",
|
|
21
|
+
"Put": "_types",
|
|
22
|
+
"Select": "_types",
|
|
23
|
+
"Fork": "_types",
|
|
24
|
+
"Delay": "_types",
|
|
25
|
+
"Retry": "_types",
|
|
26
|
+
"Timeout": "_types",
|
|
27
|
+
"TryCall": "_types",
|
|
28
|
+
"Race": "_types",
|
|
29
|
+
"All": "_types",
|
|
30
|
+
"Take": "_types",
|
|
31
|
+
"Debounce": "_types",
|
|
32
|
+
"Cmd": "_types",
|
|
33
|
+
"Batch": "_types",
|
|
34
|
+
"Sequence": "_types",
|
|
35
|
+
"TickCmd": "_types",
|
|
36
|
+
"ViewState": "_types",
|
|
37
|
+
"compact_cmds": "_types",
|
|
38
|
+
"BUILTIN_ACTIONS": "_types",
|
|
39
|
+
# Errors
|
|
40
|
+
"MiloError": "_errors",
|
|
41
|
+
"InputError": "_errors",
|
|
42
|
+
"StateError": "_errors",
|
|
43
|
+
"FormError": "_errors",
|
|
44
|
+
"AppError": "_errors",
|
|
45
|
+
"FlowError": "_errors",
|
|
46
|
+
"ConfigError": "_errors",
|
|
47
|
+
"PipelineError": "_errors",
|
|
48
|
+
"PluginError": "_errors",
|
|
49
|
+
"MCPAppError": "_errors",
|
|
50
|
+
"ErrorCode": "_errors",
|
|
51
|
+
"format_error": "_errors",
|
|
52
|
+
"format_render_error": "_errors",
|
|
53
|
+
# State
|
|
54
|
+
"Store": "state",
|
|
55
|
+
"SagaContext": "state",
|
|
56
|
+
"EffectResult": "state",
|
|
57
|
+
"combine_reducers": "state",
|
|
58
|
+
# App
|
|
59
|
+
"App": "app",
|
|
60
|
+
"run": "app",
|
|
61
|
+
"render_html": "app",
|
|
62
|
+
# Flow
|
|
63
|
+
"FlowScreen": "flow",
|
|
64
|
+
"Flow": "flow",
|
|
65
|
+
"FlowState": "flow",
|
|
66
|
+
# Form
|
|
67
|
+
"form": "form",
|
|
68
|
+
"form_reducer": "form",
|
|
69
|
+
"form_schema": "form",
|
|
70
|
+
"make_form_reducer": "form",
|
|
71
|
+
# Help
|
|
72
|
+
"HelpRenderer": "help",
|
|
73
|
+
# Dev
|
|
74
|
+
"DevServer": "dev",
|
|
75
|
+
# Commands (AI-native)
|
|
76
|
+
"CLI": "commands",
|
|
77
|
+
"CommandDef": "commands",
|
|
78
|
+
"CommandSurface": "commands",
|
|
79
|
+
"LazyCommandDef": "commands",
|
|
80
|
+
"InvokeResult": "commands",
|
|
81
|
+
# Groups
|
|
82
|
+
"Group": "groups",
|
|
83
|
+
"GroupDef": "groups",
|
|
84
|
+
"GlobalOption": "commands",
|
|
85
|
+
"RootOptionSpec": "commands",
|
|
86
|
+
# Context
|
|
87
|
+
"ConfirmStrategy": "context",
|
|
88
|
+
"Context": "context",
|
|
89
|
+
"NullOutputSink": "context",
|
|
90
|
+
"OutputSink": "context",
|
|
91
|
+
"get_context": "context",
|
|
92
|
+
"CLIProgress": "context",
|
|
93
|
+
# Config
|
|
94
|
+
"Config": "config",
|
|
95
|
+
"ConfigSpec": "config",
|
|
96
|
+
# Pipeline
|
|
97
|
+
"Pipeline": "pipeline",
|
|
98
|
+
"Phase": "pipeline",
|
|
99
|
+
"PhasePolicy": "pipeline",
|
|
100
|
+
"PhaseLog": "pipeline",
|
|
101
|
+
"PipelineState": "pipeline",
|
|
102
|
+
"PhaseStatus": "pipeline",
|
|
103
|
+
"PipelineViewState": "pipeline",
|
|
104
|
+
"make_detail_reducer": "pipeline",
|
|
105
|
+
"pipeline_to_timeline": "pipeline",
|
|
106
|
+
"set_active_pipeline": "pipeline",
|
|
107
|
+
"get_active_pipeline": "pipeline",
|
|
108
|
+
"CycleError": "pipeline",
|
|
109
|
+
# Plugins
|
|
110
|
+
"HookRegistry": "plugins",
|
|
111
|
+
"function_to_schema": "schema",
|
|
112
|
+
"validate_arguments": "schema",
|
|
113
|
+
"MinLen": "schema",
|
|
114
|
+
"MaxLen": "schema",
|
|
115
|
+
"Gt": "schema",
|
|
116
|
+
"Lt": "schema",
|
|
117
|
+
"Ge": "schema",
|
|
118
|
+
"Le": "schema",
|
|
119
|
+
"Pattern": "schema",
|
|
120
|
+
"Description": "schema",
|
|
121
|
+
"Positional": "schema",
|
|
122
|
+
"Option": "schema",
|
|
123
|
+
# Theme
|
|
124
|
+
"ThemeStyle": "theme",
|
|
125
|
+
"ThemeProxy": "theme",
|
|
126
|
+
"DEFAULT_THEME": "theme",
|
|
127
|
+
"make_style_filter": "theme",
|
|
128
|
+
"format_output": "output",
|
|
129
|
+
"write_output": "output",
|
|
130
|
+
"generate_llms_txt": "llms",
|
|
131
|
+
# Commands v2 (resources, prompts)
|
|
132
|
+
"ResourceDef": "commands",
|
|
133
|
+
"PromptDef": "commands",
|
|
134
|
+
# MCP Apps
|
|
135
|
+
"MCP_APPS_EXTENSION_ID": "mcp_apps",
|
|
136
|
+
"MCP_APPS_MIME_TYPE": "mcp_apps",
|
|
137
|
+
"MCPAppCSP": "mcp_apps",
|
|
138
|
+
"MCPAppPermissions": "mcp_apps",
|
|
139
|
+
"MCPAppResourceDef": "mcp_apps",
|
|
140
|
+
"MCPAppResourceMeta": "mcp_apps",
|
|
141
|
+
"MCPAppToolMeta": "mcp_apps",
|
|
142
|
+
"MCPAppVisibility": "mcp_apps",
|
|
143
|
+
# Middleware
|
|
144
|
+
"MCPCall": "middleware",
|
|
145
|
+
"MiddlewareStack": "middleware",
|
|
146
|
+
# Streaming
|
|
147
|
+
"Progress": "streaming",
|
|
148
|
+
# Observability
|
|
149
|
+
"RequestLog": "observability",
|
|
150
|
+
"RequestLogger": "observability",
|
|
151
|
+
# Completions
|
|
152
|
+
"install_completions": "completions",
|
|
153
|
+
# Doctor
|
|
154
|
+
"Check": "doctor",
|
|
155
|
+
"DoctorReport": "doctor",
|
|
156
|
+
"run_doctor": "doctor",
|
|
157
|
+
"format_doctor_report": "doctor",
|
|
158
|
+
# Reducer combinators
|
|
159
|
+
"quit_on": "reducers",
|
|
160
|
+
"with_cursor": "reducers",
|
|
161
|
+
"with_confirm": "reducers",
|
|
162
|
+
# Version check
|
|
163
|
+
"VersionInfo": "version_check",
|
|
164
|
+
"check_version": "version_check",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def __getattr__(name: str):
|
|
169
|
+
"""Lazy imports for public API."""
|
|
170
|
+
if name in _LAZY_IMPORTS:
|
|
171
|
+
import importlib
|
|
172
|
+
|
|
173
|
+
module = importlib.import_module(f"milo.{_LAZY_IMPORTS[name]}")
|
|
174
|
+
return getattr(module, name)
|
|
175
|
+
raise AttributeError(f"module 'milo' has no attribute {name!r}")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# Free-threaded Python marker (PEP 703)
|
|
179
|
+
def _Py_mod_gil() -> int: # noqa: N802
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
__version__ = "0.4.0"
|
|
184
|
+
__all__ = [
|
|
185
|
+
"BUILTIN_ACTIONS",
|
|
186
|
+
"CLI",
|
|
187
|
+
"DEFAULT_THEME",
|
|
188
|
+
"MCP_APPS_EXTENSION_ID",
|
|
189
|
+
"MCP_APPS_MIME_TYPE",
|
|
190
|
+
"Action",
|
|
191
|
+
"All",
|
|
192
|
+
"App",
|
|
193
|
+
"AppError",
|
|
194
|
+
"AppStatus",
|
|
195
|
+
"Batch",
|
|
196
|
+
"CLIProgress",
|
|
197
|
+
"Call",
|
|
198
|
+
"Check",
|
|
199
|
+
"Cmd",
|
|
200
|
+
"CommandDef",
|
|
201
|
+
"CommandSurface",
|
|
202
|
+
"Config",
|
|
203
|
+
"ConfigError",
|
|
204
|
+
"ConfigSpec",
|
|
205
|
+
"ConfirmStrategy",
|
|
206
|
+
"Context",
|
|
207
|
+
"CycleError",
|
|
208
|
+
"Debounce",
|
|
209
|
+
"Delay",
|
|
210
|
+
"Description",
|
|
211
|
+
"DevServer",
|
|
212
|
+
"DoctorReport",
|
|
213
|
+
"EffectResult",
|
|
214
|
+
"ErrorCode",
|
|
215
|
+
"FieldSpec",
|
|
216
|
+
"FieldState",
|
|
217
|
+
"FieldType",
|
|
218
|
+
"Flow",
|
|
219
|
+
"FlowError",
|
|
220
|
+
"FlowScreen",
|
|
221
|
+
"FlowState",
|
|
222
|
+
"Fork",
|
|
223
|
+
"FormError",
|
|
224
|
+
"FormState",
|
|
225
|
+
"Ge",
|
|
226
|
+
"GlobalOption",
|
|
227
|
+
"Group",
|
|
228
|
+
"GroupDef",
|
|
229
|
+
"Gt",
|
|
230
|
+
"HelpRenderer",
|
|
231
|
+
"HookRegistry",
|
|
232
|
+
"InputError",
|
|
233
|
+
"InvokeResult",
|
|
234
|
+
"Key",
|
|
235
|
+
"LazyCommandDef",
|
|
236
|
+
"Le",
|
|
237
|
+
"Lt",
|
|
238
|
+
"MCPAppCSP",
|
|
239
|
+
"MCPAppError",
|
|
240
|
+
"MCPAppPermissions",
|
|
241
|
+
"MCPAppResourceDef",
|
|
242
|
+
"MCPAppResourceMeta",
|
|
243
|
+
"MCPAppToolMeta",
|
|
244
|
+
"MCPAppVisibility",
|
|
245
|
+
"MCPCall",
|
|
246
|
+
"MaxLen",
|
|
247
|
+
"MiddlewareStack",
|
|
248
|
+
"MiloError",
|
|
249
|
+
"MinLen",
|
|
250
|
+
"NullOutputSink",
|
|
251
|
+
"Option",
|
|
252
|
+
"OutputSink",
|
|
253
|
+
"Pattern",
|
|
254
|
+
"Phase",
|
|
255
|
+
"PhaseLog",
|
|
256
|
+
"PhasePolicy",
|
|
257
|
+
"PhaseStatus",
|
|
258
|
+
"Pipeline",
|
|
259
|
+
"PipelineError",
|
|
260
|
+
"PipelineState",
|
|
261
|
+
"PipelineViewState",
|
|
262
|
+
"PluginError",
|
|
263
|
+
"Positional",
|
|
264
|
+
"Progress",
|
|
265
|
+
"PromptDef",
|
|
266
|
+
"Put",
|
|
267
|
+
"Quit",
|
|
268
|
+
"Race",
|
|
269
|
+
"ReducerResult",
|
|
270
|
+
"RenderTarget",
|
|
271
|
+
"RequestLog",
|
|
272
|
+
"RequestLogger",
|
|
273
|
+
"ResourceDef",
|
|
274
|
+
"Retry",
|
|
275
|
+
"RootOptionSpec",
|
|
276
|
+
"SagaContext",
|
|
277
|
+
"Screen",
|
|
278
|
+
"Select",
|
|
279
|
+
"Sequence",
|
|
280
|
+
"SpecialKey",
|
|
281
|
+
"StateError",
|
|
282
|
+
"Store",
|
|
283
|
+
"Take",
|
|
284
|
+
"ThemeProxy",
|
|
285
|
+
"ThemeStyle",
|
|
286
|
+
"TickCmd",
|
|
287
|
+
"Timeout",
|
|
288
|
+
"Transition",
|
|
289
|
+
"TryCall",
|
|
290
|
+
"VersionInfo",
|
|
291
|
+
"ViewState",
|
|
292
|
+
"check_version",
|
|
293
|
+
"combine_reducers",
|
|
294
|
+
"compact_cmds",
|
|
295
|
+
"form",
|
|
296
|
+
"form_reducer",
|
|
297
|
+
"form_schema",
|
|
298
|
+
"format_doctor_report",
|
|
299
|
+
"format_error",
|
|
300
|
+
"format_output",
|
|
301
|
+
"format_render_error",
|
|
302
|
+
"function_to_schema",
|
|
303
|
+
"generate_llms_txt",
|
|
304
|
+
"get_active_pipeline",
|
|
305
|
+
"get_context",
|
|
306
|
+
"install_completions",
|
|
307
|
+
"make_detail_reducer",
|
|
308
|
+
"make_form_reducer",
|
|
309
|
+
"make_style_filter",
|
|
310
|
+
"pipeline_to_timeline",
|
|
311
|
+
"quit_on",
|
|
312
|
+
"render_html",
|
|
313
|
+
"run",
|
|
314
|
+
"run_doctor",
|
|
315
|
+
"set_active_pipeline",
|
|
316
|
+
"validate_arguments",
|
|
317
|
+
"with_confirm",
|
|
318
|
+
"with_cursor",
|
|
319
|
+
"write_output",
|
|
320
|
+
]
|
|
@@ -35,6 +35,11 @@ def cell_width(value: object) -> int:
|
|
|
35
35
|
return width
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
def max_cell_width(value: object) -> int:
|
|
39
|
+
"""Return the widest display-cell width among the text's lines."""
|
|
40
|
+
return max((cell_width(line) for line in str(value).splitlines()), default=0)
|
|
41
|
+
|
|
42
|
+
|
|
38
43
|
def cell_ljust(value: object, width: int, fill: str = " ") -> str:
|
|
39
44
|
"""Left-justify *value* to display-cell *width*."""
|
|
40
45
|
text = str(value)
|