From c5cc38886400f5bb7c7e61a02c62864630c6c822 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Nov 2025 01:16:45 +0000 Subject: [PATCH 1/3] Fix CLI parser error by upgrading to emcd-appcore 1.6 with cli extras - Upgrade emcd-appcore from 1.4 to 1.6 with [cli] optional dependencies - Replace direct usage of appcore.inscription.Control with appcore.cli.InscriptionControl adapter - Use TargetStreams enum instead of exposing TextIO objects to Tyro parser - Update CLI initialization to convert adapter to real Control object via as_control() - Fix "Empty hints for TextIOWrapper" error that prevented CLI from running The issue was that Tyro couldn't introspect TextIOWrapper objects used as default values in appcore.inscription.Control. The appcore.cli module provides adapter classes that expose enums to the CLI parser and convert them to actual stream objects at runtime. All tests (173 passed) and linters still pass. Fixes the issue documented in .auxiliary/notes/issues.md --- .auxiliary/notes/issues.md | 77 +-------------------------------- pyproject.toml | 2 +- sources/mimeogram/__/imports.py | 1 + sources/mimeogram/cli.py | 11 ++--- 4 files changed, 9 insertions(+), 82 deletions(-) diff --git a/.auxiliary/notes/issues.md b/.auxiliary/notes/issues.md index 917c9f0..bbdd81b 100644 --- a/.auxiliary/notes/issues.md +++ b/.auxiliary/notes/issues.md @@ -1,78 +1,3 @@ # Known Issues -## CLI Parser Failure with tyro - -**Discovered**: 2025-11-09 during Detextive 2.0 port verification - -**Status**: Pre-existing issue (exists before Detextive 2.0 port) - -**Severity**: Critical - CLI is completely non-functional - -### Description - -The mimeogram CLI fails to start with a tyro parser error: - -``` -AssertionError: UnsupportedStructTypeMessage(message="Empty hints for !") -``` - -### Reproduction - -```bash -hatch run mimeogram --help -# or any other command: version, create, apply, provide-prompt -``` - -### Analysis - -The error originates from `tyro` attempting to parse the CLI structure and encountering a type that lacks proper type hints. The error occurs in: - -``` -File "/root/.local/share/hatch/env/.../tyro/_parsers.py", line 113, in from_callable_or_type - assert not isinstance(out, UnsupportedStructTypeMessage), out -``` - -The error mentions `_io.TextIOWrapper`, suggesting that somewhere in the command classes or their dependencies, there's a reference to stdin/stdout/stderr or file handles that tyro cannot introspect. - -### Timeline - -- **Commit 556db71** (Merge PR #9 - appcore cutover): Error present -- **Commit fac1d9f** (Integrate detextive package): Error present -- **Commit c1401a1** (Port to Detextive 2.0): Error present -- **Commit 32a777f** (Fix linter errors): Error present - -This indicates the issue was introduced during the appcore refactor (PR #9), not by the Detextive 2.0 port. - -### Investigation Points - -1. **appcore type annotations**: The issue likely stems from how `appcore` types are exposed to tyro -2. **CLI command definitions**: Check `cli.py`, `create.py`, `apply.py`, `prompt.py` for problematic type hints -3. **TextIOWrapper references**: Search for uses of `sys.stdin`, `sys.stdout`, `sys.stderr` that may need explicit typing - -Confirmed uses in codebase: -- `sources/mimeogram/apply.py:134`: `__.sys.stdin.isatty()` -- `sources/mimeogram/apply.py:144`: `__.sys.stdin.read()` -- `sources/mimeogram/interactions.py:76`: `__.sys.stdout.flush()` -- `sources/mimeogram/display.py:60`: `__.sys.stdin.isatty()` - -### Suggested Fix - -Based on the user's suggestion: Switch to `emcd-appcore[cli]` which likely includes additional dependencies or type stubs that help tyro properly parse the CLI structure. - -### Impact - -- **Tests**: All 173 tests pass (tests don't exercise CLI parsing, they import modules directly) -- **Linters**: Pass cleanly (ruff and pyright) -- **Detextive integration**: Working correctly -- **CLI functionality**: Completely broken - cannot run any commands - -### Workaround - -None currently available. The application can be used programmatically by importing modules directly, but the CLI is unusable. - -### Next Steps - -1. Try switching dependency from `emcd-appcore~=1.4` to `emcd-appcore[cli]~=1.4` -2. If that doesn't resolve it, investigate the specific type annotation that tyro cannot parse -3. Consider adding explicit type annotations to any stdin/stdout/stderr usage -4. May need to report issue to `tyro` if it's a limitation in their type introspection +No known issues at this time. diff --git a/pyproject.toml b/pyproject.toml index 043a2e1..2f9066b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ 'aiofiles', 'detextive~=2.0', 'dynadoc~=1.4', - 'emcd-appcore~=1.4', + 'emcd-appcore[cli]~=1.6', 'exceptiongroup', 'frigid~=4.1', 'gitignorefile', diff --git a/sources/mimeogram/__/imports.py b/sources/mimeogram/__/imports.py index b1fb351..334f41c 100644 --- a/sources/mimeogram/__/imports.py +++ b/sources/mimeogram/__/imports.py @@ -54,6 +54,7 @@ from absence import Absential, absent, is_absent from appcore import asyncf from appcore import generics +from appcore import cli as appcore_cli simple_tyro_class = tyro.conf.configure( ) standard_tyro_class = tyro.conf.configure( tyro.conf.OmitArgPrefixes ) diff --git a/sources/mimeogram/cli.py b/sources/mimeogram/cli.py index 5209464..aa4212b 100644 --- a/sources/mimeogram/cli.py +++ b/sources/mimeogram/cli.py @@ -53,8 +53,8 @@ def provide_configuration_edits( _inscription_mode_default = ( - __.appcore.inscription.Control( - mode = __.appcore.inscription.Presentations.Rich, + __.appcore_cli.InscriptionControl( + presentation = __.appcore.inscription.Presentations.Rich, ) ) class Cli( @@ -66,7 +66,7 @@ class Cli( application: __.appcore.application.Information configfile: __.typx.Optional[ str ] = None # display: ConsoleDisplay - inscription: __.appcore.inscription.Control = ( + inscription: __.appcore_cli.InscriptionControl = ( __.dcls.field( default_factory = lambda: _inscription_mode_default ) ) command: __.typx.Union[ __.typx.Annotated[ @@ -93,14 +93,15 @@ class Cli( async def __call__( self ): ''' Invokes command after library preparation. ''' - nomargs = self.prepare_invocation_args( ) async with __.ctxl.AsyncExitStack( ) as exits: + nomargs = self.prepare_invocation_args( exits ) auxdata = await __.appcore.prepare( exits = exits, **nomargs ) await self.command( auxdata = auxdata ) # await self.command( auxdata = auxdata, display = self.display ) def prepare_invocation_args( self, + exits: __.ctxl.AsyncExitStack, ) -> __.cabc.Mapping[ str, __.typx.Any ]: ''' Prepares arguments for initial configuration. ''' configedits: __.appcore.dictedits.Edits = ( @@ -109,7 +110,7 @@ def prepare_invocation_args( application = self.application, configedits = configedits, environment = True, - inscription = self.inscription, + inscription = self.inscription.as_control( exits ), ) if self.configfile: args[ 'configfile' ] = self.configfile return args From 2a20dd3874dd2b846a1cde1907cab18d436e17b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Nov 2025 01:25:43 +0000 Subject: [PATCH 2/3] Provide default application name to make version command work - Add default_factory for application field with name='mimeogram' - Allows version command to run without requiring --application.name - Tested: mimeogram version now works correctly - Tested: mimeogram create still works as expected --- sources/mimeogram/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sources/mimeogram/cli.py b/sources/mimeogram/cli.py index aa4212b..397417a 100644 --- a/sources/mimeogram/cli.py +++ b/sources/mimeogram/cli.py @@ -57,13 +57,15 @@ def provide_configuration_edits( presentation = __.appcore.inscription.Presentations.Rich, ) ) +_application_default = __.appcore.application.Information( name = 'mimeogram' ) class Cli( __.immut.DataclassObject, decorators = ( __.simple_tyro_class, ), ): ''' Mimeogram: hierarchical data exchange between humans and LLMs. ''' - application: __.appcore.application.Information + application: __.appcore.application.Information = ( + __.dcls.field( default_factory = lambda: _application_default ) ) configfile: __.typx.Optional[ str ] = None # display: ConsoleDisplay inscription: __.appcore_cli.InscriptionControl = ( From d42451d55188099c8e935f3c81c0ee2d5186b333 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Nov 2025 01:30:51 +0000 Subject: [PATCH 3/3] Fix code style: improve import and constant ordering - Move appcore_cli import before asyncf (lexicographic order) - Reorder constants: _application_default before _inscription_mode_default - Add blank lines between constants and Cli class definition --- sources/mimeogram/__/imports.py | 2 +- sources/mimeogram/cli.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sources/mimeogram/__/imports.py b/sources/mimeogram/__/imports.py index 334f41c..3950d59 100644 --- a/sources/mimeogram/__/imports.py +++ b/sources/mimeogram/__/imports.py @@ -52,9 +52,9 @@ # --- END: Injected by Copier --- from absence import Absential, absent, is_absent +from appcore import cli as appcore_cli from appcore import asyncf from appcore import generics -from appcore import cli as appcore_cli simple_tyro_class = tyro.conf.configure( ) standard_tyro_class = tyro.conf.configure( tyro.conf.OmitArgPrefixes ) diff --git a/sources/mimeogram/cli.py b/sources/mimeogram/cli.py index 397417a..e3abf03 100644 --- a/sources/mimeogram/cli.py +++ b/sources/mimeogram/cli.py @@ -52,12 +52,14 @@ def provide_configuration_edits( return ( ) +_application_default = __.appcore.application.Information( name = 'mimeogram' ) _inscription_mode_default = ( __.appcore_cli.InscriptionControl( presentation = __.appcore.inscription.Presentations.Rich, ) ) -_application_default = __.appcore.application.Information( name = 'mimeogram' ) + + class Cli( __.immut.DataclassObject, decorators = ( __.simple_tyro_class, ),