milo-cli 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.
Files changed (88) hide show
  1. milo_cli-0.1.0/PKG-INFO +441 -0
  2. milo_cli-0.1.0/README.md +414 -0
  3. milo_cli-0.1.0/pyproject.toml +116 -0
  4. milo_cli-0.1.0/setup.cfg +4 -0
  5. milo_cli-0.1.0/src/milo/__init__.py +183 -0
  6. milo_cli-0.1.0/src/milo/_child.py +141 -0
  7. milo_cli-0.1.0/src/milo/_errors.py +189 -0
  8. milo_cli-0.1.0/src/milo/_protocols.py +37 -0
  9. milo_cli-0.1.0/src/milo/_types.py +234 -0
  10. milo_cli-0.1.0/src/milo/app.py +353 -0
  11. milo_cli-0.1.0/src/milo/cli.py +134 -0
  12. milo_cli-0.1.0/src/milo/commands.py +951 -0
  13. milo_cli-0.1.0/src/milo/config.py +250 -0
  14. milo_cli-0.1.0/src/milo/context.py +81 -0
  15. milo_cli-0.1.0/src/milo/dev.py +238 -0
  16. milo_cli-0.1.0/src/milo/flow.py +146 -0
  17. milo_cli-0.1.0/src/milo/form.py +277 -0
  18. milo_cli-0.1.0/src/milo/gateway.py +393 -0
  19. milo_cli-0.1.0/src/milo/groups.py +194 -0
  20. milo_cli-0.1.0/src/milo/help.py +84 -0
  21. milo_cli-0.1.0/src/milo/input/__init__.py +6 -0
  22. milo_cli-0.1.0/src/milo/input/_platform.py +81 -0
  23. milo_cli-0.1.0/src/milo/input/_reader.py +93 -0
  24. milo_cli-0.1.0/src/milo/input/_sequences.py +63 -0
  25. milo_cli-0.1.0/src/milo/llms.py +172 -0
  26. milo_cli-0.1.0/src/milo/mcp.py +299 -0
  27. milo_cli-0.1.0/src/milo/middleware.py +67 -0
  28. milo_cli-0.1.0/src/milo/observability.py +111 -0
  29. milo_cli-0.1.0/src/milo/output.py +106 -0
  30. milo_cli-0.1.0/src/milo/pipeline.py +276 -0
  31. milo_cli-0.1.0/src/milo/plugins.py +168 -0
  32. milo_cli-0.1.0/src/milo/py.typed +0 -0
  33. milo_cli-0.1.0/src/milo/registry.py +213 -0
  34. milo_cli-0.1.0/src/milo/schema.py +214 -0
  35. milo_cli-0.1.0/src/milo/state.py +229 -0
  36. milo_cli-0.1.0/src/milo/streaming.py +41 -0
  37. milo_cli-0.1.0/src/milo/templates/__init__.py +38 -0
  38. milo_cli-0.1.0/src/milo/templates/error.kida +5 -0
  39. milo_cli-0.1.0/src/milo/templates/field_confirm.kida +1 -0
  40. milo_cli-0.1.0/src/milo/templates/field_select.kida +3 -0
  41. milo_cli-0.1.0/src/milo/templates/field_text.kida +1 -0
  42. milo_cli-0.1.0/src/milo/templates/form.kida +8 -0
  43. milo_cli-0.1.0/src/milo/templates/help.kida +7 -0
  44. milo_cli-0.1.0/src/milo/templates/progress.kida +1 -0
  45. milo_cli-0.1.0/src/milo/testing/__init__.py +27 -0
  46. milo_cli-0.1.0/src/milo/testing/_mcp.py +87 -0
  47. milo_cli-0.1.0/src/milo/testing/_record.py +125 -0
  48. milo_cli-0.1.0/src/milo/testing/_replay.py +68 -0
  49. milo_cli-0.1.0/src/milo/testing/_snapshot.py +96 -0
  50. milo_cli-0.1.0/src/milo_cli.egg-info/PKG-INFO +441 -0
  51. milo_cli-0.1.0/src/milo_cli.egg-info/SOURCES.txt +86 -0
  52. milo_cli-0.1.0/src/milo_cli.egg-info/dependency_links.txt +1 -0
  53. milo_cli-0.1.0/src/milo_cli.egg-info/entry_points.txt +2 -0
  54. milo_cli-0.1.0/src/milo_cli.egg-info/requires.txt +10 -0
  55. milo_cli-0.1.0/src/milo_cli.egg-info/top_level.txt +1 -0
  56. milo_cli-0.1.0/tests/test_ai_native.py +664 -0
  57. milo_cli-0.1.0/tests/test_app.py +408 -0
  58. milo_cli-0.1.0/tests/test_child.py +108 -0
  59. milo_cli-0.1.0/tests/test_cli.py +169 -0
  60. milo_cli-0.1.0/tests/test_components.py +206 -0
  61. milo_cli-0.1.0/tests/test_config.py +285 -0
  62. milo_cli-0.1.0/tests/test_context.py +314 -0
  63. milo_cli-0.1.0/tests/test_dev.py +297 -0
  64. milo_cli-0.1.0/tests/test_effects.py +106 -0
  65. milo_cli-0.1.0/tests/test_errors.py +106 -0
  66. milo_cli-0.1.0/tests/test_flow.py +170 -0
  67. milo_cli-0.1.0/tests/test_form.py +438 -0
  68. milo_cli-0.1.0/tests/test_groups.py +329 -0
  69. milo_cli-0.1.0/tests/test_help.py +99 -0
  70. milo_cli-0.1.0/tests/test_input.py +369 -0
  71. milo_cli-0.1.0/tests/test_lazy.py +332 -0
  72. milo_cli-0.1.0/tests/test_mcp_prompts.py +86 -0
  73. milo_cli-0.1.0/tests/test_mcp_resources.py +84 -0
  74. milo_cli-0.1.0/tests/test_middleware.py +122 -0
  75. milo_cli-0.1.0/tests/test_milo_init.py +273 -0
  76. milo_cli-0.1.0/tests/test_mount.py +97 -0
  77. milo_cli-0.1.0/tests/test_observability.py +134 -0
  78. milo_cli-0.1.0/tests/test_pipeline.py +299 -0
  79. milo_cli-0.1.0/tests/test_plugins.py +214 -0
  80. milo_cli-0.1.0/tests/test_protocols.py +106 -0
  81. milo_cli-0.1.0/tests/test_registry_v2.py +126 -0
  82. milo_cli-0.1.0/tests/test_schema_v2.py +207 -0
  83. milo_cli-0.1.0/tests/test_state.py +352 -0
  84. milo_cli-0.1.0/tests/test_streaming.py +84 -0
  85. milo_cli-0.1.0/tests/test_templates.py +92 -0
  86. milo_cli-0.1.0/tests/test_testing.py +389 -0
  87. milo_cli-0.1.0/tests/test_testing_mcp.py +118 -0
  88. milo_cli-0.1.0/tests/test_types.py +222 -0
@@ -0,0 +1,441 @@
1
+ Metadata-Version: 2.4
2
+ Name: milo-cli
3
+ Version: 0.1.0
4
+ Summary: Template-driven CLI applications for free-threaded Python
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://lbliii.github.io/milo/
7
+ Project-URL: Documentation, https://lbliii.github.io/milo/
8
+ Project-URL: Repository, https://github.com/lbliii/milo
9
+ Project-URL: Changelog, https://github.com/lbliii/milo/blob/main/CHANGELOG.md
10
+ Keywords: cli,terminal,forms,free-threading,template,elm
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Terminals
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.14
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: kida-templates>=0.3.0
21
+ Provides-Extra: docs
22
+ Requires-Dist: bengal>=0.2.6; extra == "docs"
23
+ Provides-Extra: yaml
24
+ Requires-Dist: pyyaml>=6.0; extra == "yaml"
25
+ Provides-Extra: watch
26
+ Requires-Dist: watchfiles>=1.0; extra == "watch"
27
+
28
+ # ᗣᗣ Milo
29
+
30
+ [![PyPI version](https://img.shields.io/pypi/v/milo.svg)](https://pypi.org/project/milo/)
31
+ [![Build Status](https://github.com/lbliii/milo/actions/workflows/tests.yml/badge.svg)](https://github.com/lbliii/milo/actions/workflows/tests.yml)
32
+ [![Python 3.14+](https://img.shields.io/badge/python-3.14+-blue.svg)](https://pypi.org/project/milo/)
33
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
34
+
35
+ **Template-driven CLI applications for free-threaded Python**
36
+
37
+ ```python
38
+ from milo import App, Action
39
+
40
+ def reducer(state, action):
41
+ if state is None:
42
+ return {"count": 0}
43
+ if action.type == "@@KEY" and action.payload.char == " ":
44
+ return {**state, "count": state["count"] + 1}
45
+ return state
46
+
47
+ app = App(template="counter.kida", reducer=reducer, initial_state=None)
48
+ final_state = app.run()
49
+ ```
50
+
51
+ ---
52
+
53
+ ## What is Milo?
54
+
55
+ Milo is a framework for building interactive terminal applications in Python 3.14t. It uses the Elm Architecture (Model-View-Update) — an immutable state tree managed by pure reducer functions, a view layer driven by Kida templates, and generator-based sagas for side effects. The result is CLI apps that are predictable, testable, and free-threading ready.
56
+
57
+ **Why people pick it:**
58
+
59
+ - **Elm Architecture** — Immutable state, pure reducers, declarative views. Every state transition is explicit and testable.
60
+ - **Template-driven UI** — Render terminal output with Kida templates. Same syntax you use for HTML, now for CLI.
61
+ - **Free-threading ready** — Built for Python 3.14t (PEP 703). Sagas run on `ThreadPoolExecutor` with no GIL contention.
62
+ - **Declarative flows** — Chain multi-screen state machines with the `>>` operator. No manual navigation plumbing.
63
+ - **Built-in forms** — Text, select, confirm, and password fields with validation, keyboard navigation, and TTY fallback.
64
+ - **One runtime dependency** — Just `kida-templates`. No click, no rich, no curses.
65
+
66
+ ## Use Milo For
67
+
68
+ - **Interactive CLI tools** — Wizards, installers, configuration prompts, and guided workflows
69
+ - **Multi-screen terminal apps** — Declarative flows with `>>` operator for screen-to-screen navigation
70
+ - **Forms and data collection** — Text, select, confirm, and password fields with validation
71
+ - **Dev tools with hot reload** — `milo dev` watches templates and live-reloads on change
72
+ - **Session recording and replay** — Record user sessions to JSONL, replay for debugging or CI regression tests
73
+ - **Styled terminal output** — Kida terminal templates with ANSI colors, progress bars, and live rendering
74
+ - **AI agent integration** — Every CLI is an MCP server; register multiple CLIs behind a single gateway
75
+
76
+ ---
77
+
78
+ ## Installation
79
+
80
+ ```bash
81
+ pip install milo
82
+ ```
83
+
84
+ Requires Python 3.14+
85
+
86
+ ---
87
+
88
+ ## Quick Start
89
+
90
+ | Function | Description |
91
+ |----------|-------------|
92
+ | `App(template, reducer, initial_state)` | Create a single-screen app |
93
+ | `App.from_flow(flow)` | Create a multi-screen app from a `Flow` |
94
+ | `app.run()` | Run the event loop, return final state |
95
+ | `Store(reducer, initial_state)` | Standalone state container |
96
+ | `combine_reducers(**reducers)` | Compose slice-based reducers |
97
+ | `form(*specs)` | Run an interactive form, return `{field: value}` |
98
+ | `FlowScreen(name, template, reducer)` | Define a named screen |
99
+ | `flow = screen_a >> screen_b` | Chain screens into a flow |
100
+ | `render_html(state, template)` | One-shot static HTML render |
101
+ | `DevServer(app, watch_dirs)` | Hot-reload dev server |
102
+
103
+ ---
104
+
105
+ ## Features
106
+
107
+ | Feature | Description | Docs |
108
+ |---------|-------------|------|
109
+ | **State Management** | Redux-style `Store` with dispatch, listeners, middleware, and saga scheduling | [State →](https://lbliii.github.io/milo/docs/usage/state/) |
110
+ | **Sagas** | Generator-based side effects: `Call`, `Put`, `Select`, `Fork`, `Delay` | [Sagas →](https://lbliii.github.io/milo/docs/usage/sagas/) |
111
+ | **Flows** | Multi-screen state machines with `>>` operator and custom transitions | [Flows →](https://lbliii.github.io/milo/docs/usage/flows/) |
112
+ | **Forms** | Text, select, confirm, password fields with validation and TTY fallback | [Forms →](https://lbliii.github.io/milo/docs/usage/forms/) |
113
+ | **Input Handling** | Cross-platform key reader with full escape sequence support (arrows, F-keys, modifiers) | [Input →](https://lbliii.github.io/milo/docs/usage/input/) |
114
+ | **Templates** | Kida-powered terminal rendering with built-in form, field, help, and progress templates | [Templates →](https://lbliii.github.io/milo/docs/usage/templates/) |
115
+ | **Dev Server** | `milo dev` with filesystem polling and `@@HOT_RELOAD` dispatch | [Dev →](https://lbliii.github.io/milo/docs/usage/dev/) |
116
+ | **Session Recording** | JSONL action log with state hashes for debugging and regression testing | [Testing →](https://lbliii.github.io/milo/docs/usage/testing/) |
117
+ | **Replay** | Time-travel debugging, speed control, step-by-step mode, CI hash assertions | [Testing →](https://lbliii.github.io/milo/docs/usage/testing/) |
118
+ | **Snapshot Testing** | `assert_renders`, `assert_state`, `assert_saga` for deterministic test coverage | [Testing →](https://lbliii.github.io/milo/docs/usage/testing/) |
119
+ | **Help Rendering** | `HelpRenderer` — drop-in `argparse.HelpFormatter` using Kida templates | [Help →](https://lbliii.github.io/milo/docs/usage/help/) |
120
+ | **MCP Server** | Every CLI doubles as an MCP server — AI agents discover and call commands via JSON-RPC | [MCP →](https://lbliii.github.io/milo/docs/usage/mcp/) |
121
+ | **MCP Gateway** | Single gateway aggregates all registered Milo CLIs for unified AI agent access | [MCP →](https://lbliii.github.io/milo/docs/usage/mcp/) |
122
+ | **llms.txt** | Generate AI-readable discovery documents from CLI command definitions | [llms.txt →](https://lbliii.github.io/milo/docs/usage/llms/) |
123
+ | **Error System** | Structured error hierarchy with namespaced codes (`M-INP-001`, `M-STA-003`) | [Errors →](https://lbliii.github.io/milo/docs/reference/errors/) |
124
+
125
+ ---
126
+
127
+ ## Usage
128
+
129
+ <details>
130
+ <summary><strong>Single-Screen App</strong> — Counter with keyboard input</summary>
131
+
132
+ ```python
133
+ from milo import App, Action
134
+
135
+ def reducer(state, action):
136
+ if state is None:
137
+ return {"count": 0}
138
+ if action.type == "@@KEY" and action.payload.char == " ":
139
+ return {**state, "count": state["count"] + 1}
140
+ return state
141
+
142
+ app = App(template="counter.kida", reducer=reducer, initial_state=None)
143
+ final_state = app.run()
144
+ ```
145
+
146
+ **counter.kida:**
147
+ ```
148
+ Count: {{ count }}
149
+
150
+ Press SPACE to increment, Ctrl+C to quit.
151
+ ```
152
+
153
+ </details>
154
+
155
+ <details>
156
+ <summary><strong>Multi-Screen Flow</strong> — Chain screens with <code>>></code></summary>
157
+
158
+ ```python
159
+ from milo import App
160
+ from milo.flow import FlowScreen
161
+
162
+ welcome = FlowScreen("welcome", "welcome.kida", welcome_reducer)
163
+ config = FlowScreen("config", "config.kida", config_reducer)
164
+ confirm = FlowScreen("confirm", "confirm.kida", confirm_reducer)
165
+
166
+ flow = welcome >> config >> confirm
167
+ app = App.from_flow(flow)
168
+ app.run()
169
+ ```
170
+
171
+ Navigate between screens by dispatching `@@NAVIGATE` actions from your reducers. Add custom transitions with `flow.with_transition("welcome", "confirm", on="@@SKIP")`.
172
+
173
+ </details>
174
+
175
+ <details>
176
+ <summary><strong>Interactive Forms</strong> — Collect structured input</summary>
177
+
178
+ ```python
179
+ from milo import form, FieldSpec, FieldType
180
+
181
+ result = form(
182
+ FieldSpec("name", "Your name"),
183
+ FieldSpec("env", "Environment", field_type=FieldType.SELECT,
184
+ choices=("dev", "staging", "prod")),
185
+ FieldSpec("confirm", "Deploy?", field_type=FieldType.CONFIRM),
186
+ )
187
+ # result = {"name": "Alice", "env": "prod", "confirm": True}
188
+ ```
189
+
190
+ Tab/Shift+Tab navigates fields. Arrow keys cycle select options. Falls back to plain `input()` prompts when stdin is not a TTY.
191
+
192
+ </details>
193
+
194
+ <details>
195
+ <summary><strong>Sagas</strong> — Generator-based side effects</summary>
196
+
197
+ ```python
198
+ from milo import Call, Put, Select, ReducerResult
199
+
200
+ def fetch_saga():
201
+ url = yield Select(lambda s: s["url"])
202
+ data = yield Call(fetch_json, (url,))
203
+ yield Put(Action("FETCH_DONE", payload=data))
204
+
205
+ def reducer(state, action):
206
+ if action.type == "@@KEY" and action.payload.char == "f":
207
+ return ReducerResult({**state, "loading": True}, sagas=(fetch_saga,))
208
+ if action.type == "FETCH_DONE":
209
+ return {**state, "loading": False, "data": action.payload}
210
+ return state
211
+ ```
212
+
213
+ Effects: `Call(fn, args)`, `Put(action)`, `Select(selector)`, `Fork(saga)`, `Delay(seconds)`.
214
+
215
+ </details>
216
+
217
+ <details>
218
+ <summary><strong>Middleware</strong> — Intercept and transform dispatches</summary>
219
+
220
+ ```python
221
+ def logging_middleware(dispatch, get_state):
222
+ def wrapper(action):
223
+ print(f"Action: {action.type}")
224
+ return dispatch(action)
225
+ return wrapper
226
+
227
+ app = App(
228
+ template="app.kida",
229
+ reducer=reducer,
230
+ initial_state=None,
231
+ middleware=[logging_middleware],
232
+ )
233
+ ```
234
+
235
+ </details>
236
+
237
+ <details>
238
+ <summary><strong>Dev Server</strong> — Hot reload templates</summary>
239
+
240
+ ```bash
241
+ # Watch templates and reload on change
242
+ milo dev myapp:app --watch ./templates --poll 0.25
243
+ ```
244
+
245
+ ```python
246
+ from milo import App, DevServer
247
+
248
+ app = App(template="dashboard.kida", reducer=reducer, initial_state=None)
249
+ server = DevServer(app, watch_dirs=("./templates",), poll_interval=0.5)
250
+ server.run()
251
+ ```
252
+
253
+ </details>
254
+
255
+ <details>
256
+ <summary><strong>Session Recording & Replay</strong> — Debug and regression testing</summary>
257
+
258
+ ```python
259
+ # Record a session
260
+ app = App(template="app.kida", reducer=reducer, initial_state=None, record=True)
261
+ app.run() # Writes to session.jsonl
262
+
263
+ # Replay for debugging
264
+ milo replay session.jsonl --speed 2.0 --diff
265
+
266
+ # CI regression: assert state hashes match
267
+ milo replay session.jsonl --assert --reducer myapp:reducer
268
+
269
+ # Step-by-step interactive replay
270
+ milo replay session.jsonl --step
271
+ ```
272
+
273
+ </details>
274
+
275
+ <details>
276
+ <summary><strong>Testing Utilities</strong> — Snapshot, state, and saga assertions</summary>
277
+
278
+ ```python
279
+ from milo.testing import assert_renders, assert_state, assert_saga
280
+ from milo import Action, Call
281
+
282
+ # Snapshot test: render state through template, compare to file
283
+ assert_renders({"count": 5}, "counter.kida", snapshot="tests/snapshots/count_5.txt")
284
+
285
+ # Reducer test: feed actions, assert final state
286
+ assert_state(reducer, None, [Action("@@INIT"), Action("INCREMENT")], {"count": 1})
287
+
288
+ # Saga test: step through generator, assert each yielded effect
289
+ assert_saga(my_saga(), [(Call(fetch, ("url",), {}), {"data": 42})])
290
+ ```
291
+
292
+ Set `MILO_UPDATE_SNAPSHOTS=1` to regenerate snapshot files.
293
+
294
+ </details>
295
+
296
+ <details>
297
+ <summary><strong>MCP Server & Gateway</strong> — AI agent integration</summary>
298
+
299
+ Every Milo CLI is automatically an MCP server:
300
+
301
+ ```bash
302
+ # Run as MCP server (stdin/stdout JSON-RPC)
303
+ myapp --mcp
304
+
305
+ # Register with an AI host directly
306
+ claude mcp add myapp -- uv run python examples/taskman/app.py --mcp
307
+ ```
308
+
309
+ For multiple CLIs, register them and run a single gateway:
310
+
311
+ ```bash
312
+ # Register CLIs
313
+ taskman --mcp-install
314
+ ghub --mcp-install
315
+
316
+ # Run the unified gateway
317
+ uv run python -m milo.gateway --mcp
318
+
319
+ # Or register the gateway with your AI host
320
+ claude mcp add milo -- uv run python -m milo.gateway --mcp
321
+ ```
322
+
323
+ The gateway namespaces tools automatically: `taskman.add`, `ghub.repo.list`, etc. Implements MCP 2025-11-25 with `outputSchema`, `structuredContent`, and tool `title` fields.
324
+
325
+ </details>
326
+
327
+ ---
328
+
329
+ ## Architecture
330
+
331
+ <details>
332
+ <summary><strong>Elm Architecture</strong> — Model-View-Update loop</summary>
333
+
334
+ ```
335
+ ┌──────────────┐
336
+ │ Terminal │
337
+ │ (View) │
338
+ └──────┬───────┘
339
+ │ Key events
340
+
341
+ ┌──────────┐ ┌──────────────────┐ ┌──────────────┐
342
+ │ Kida │◄───│ Store │◄───│ Reducer │
343
+ │ Template │ │ (State Tree) │ │ (Pure fn) │
344
+ └──────────┘ └──────────┬───────┘ └──────────────┘
345
+
346
+
347
+ ┌──────────────┐
348
+ │ Sagas │
349
+ │ (Side Effects│
350
+ │ on ThreadPool)
351
+ └──────────────┘
352
+ ```
353
+
354
+ 1. **Model** — Immutable state (plain dicts or frozen dataclasses)
355
+ 2. **View** — Kida templates render state to terminal output
356
+ 3. **Update** — Pure `reducer(state, action) -> state` functions
357
+ 4. **Effects** — Generator-based sagas scheduled on `ThreadPoolExecutor`
358
+
359
+ </details>
360
+
361
+ <details>
362
+ <summary><strong>Event Loop</strong> — App lifecycle</summary>
363
+
364
+ ```
365
+ App.run()
366
+ ├── Store(reducer, initial_state)
367
+ ├── KeyReader (raw mode, escape sequences → Key objects)
368
+ ├── TerminalRenderer (alternate screen buffer, flicker-free updates)
369
+ ├── Optional: tick thread (@@TICK at interval)
370
+ ├── Optional: SIGWINCH handler (@@RESIZE)
371
+ └── Loop:
372
+ read key → dispatch @@KEY → reducer → re-render
373
+ until state.submitted or @@QUIT
374
+ ```
375
+
376
+ </details>
377
+
378
+ <details>
379
+ <summary><strong>Builtin Actions</strong> — Event vocabulary</summary>
380
+
381
+ | Action | Trigger | Payload |
382
+ |--------|---------|---------|
383
+ | `@@INIT` | Store creation | — |
384
+ | `@@KEY` | Keyboard input | `Key(char, name, ctrl, alt, shift)` |
385
+ | `@@TICK` | Timer interval | — |
386
+ | `@@RESIZE` | Terminal resize | `(cols, rows)` |
387
+ | `@@NAVIGATE` | Screen transition | `screen_name` |
388
+ | `@@HOT_RELOAD` | Template file change | `file_path` |
389
+ | `@@EFFECT_RESULT` | Saga completion | `result` |
390
+ | `@@QUIT` | Ctrl+C | — |
391
+
392
+ </details>
393
+
394
+ ---
395
+
396
+ ## Documentation
397
+
398
+ | Section | Description |
399
+ |---------|-------------|
400
+ | [Get Started](https://lbliii.github.io/milo/docs/get-started/) | Installation and quickstart |
401
+ | [Usage](https://lbliii.github.io/milo/docs/usage/) | State, sagas, flows, forms, templates |
402
+ | [Testing](https://lbliii.github.io/milo/docs/usage/testing/) | Snapshots, recording, replay |
403
+ | [MCP & AI](https://lbliii.github.io/milo/docs/usage/mcp/) | MCP server, gateway, and llms.txt |
404
+ | [Reference](https://lbliii.github.io/milo/docs/reference/) | Complete API documentation |
405
+
406
+ ---
407
+
408
+ ## Development
409
+
410
+ ```bash
411
+ git clone https://github.com/lbliii/milo.git
412
+ cd milo
413
+ # Uses Python 3.14t by default (.python-version)
414
+ uv sync --group dev --python 3.14t
415
+ PYTHON_GIL=0 uv run --python 3.14t pytest
416
+ ```
417
+
418
+ ---
419
+
420
+ ## The Bengal Ecosystem
421
+
422
+ A structured reactive stack — every layer written in pure Python for 3.14t free-threading.
423
+
424
+ | | | | |
425
+ |--:|---|---|---|
426
+ | **ᓚᘏᗢ** | [Bengal](https://github.com/lbliii/bengal) | Static site generator | [Docs](https://lbliii.github.io/bengal/) |
427
+ | **∿∿** | [Purr](https://github.com/lbliii/purr) | Content runtime | — |
428
+ | **⌁⌁** | [Chirp](https://github.com/lbliii/chirp) | Web framework | [Docs](https://lbliii.github.io/chirp/) |
429
+ | **=^..^=** | [Pounce](https://github.com/lbliii/pounce) | ASGI server | [Docs](https://lbliii.github.io/pounce/) |
430
+ | **)彡** | [Kida](https://github.com/lbliii/kida) | Template engine | [Docs](https://lbliii.github.io/kida/) |
431
+ | **ฅᨐฅ** | [Patitas](https://github.com/lbliii/patitas) | Markdown parser | [Docs](https://lbliii.github.io/patitas/) |
432
+ | **⌾⌾⌾** | [Rosettes](https://github.com/lbliii/rosettes) | Syntax highlighter | [Docs](https://lbliii.github.io/rosettes/) |
433
+ | **ᗣᗣ** | **Milo** | CLI framework ← You are here | [Docs](https://lbliii.github.io/milo/) |
434
+
435
+ Python-native. Free-threading ready. No npm required.
436
+
437
+ ---
438
+
439
+ ## License
440
+
441
+ MIT License — see [LICENSE](LICENSE) for details.