feat(wren): add context init --from-mdl to import MDL JSON as YAML project#1516
feat(wren): add context init --from-mdl to import MDL JSON as YAML project#1516goldmedal merged 1 commit intofeat/cli-0.2.0from
Conversation
…oject Adds --from-mdl flag to `wren context init` so users with legacy mdl.json files can migrate to the v2 YAML project workflow in one command. - context.py: _camel_to_snake, _convert_keys_to_snake, ProjectFile, convert_mdl_to_project, write_project_files - context_cli.py: extend init with --from-mdl and --force flags - tests/unit/test_convert_mdl.py: 25 tests (unit, integration, round-trip, CLI, edge cases) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
wren/src/wren/context_cli.py (1)
59-65: Add error handling for invalid JSON.If the MDL file contains malformed JSON,
json.loadsraisesJSONDecodeErrorwith a raw traceback. Wrapping this in a try-except would provide a cleaner user experience.🛡️ Proposed fix to handle JSON parse errors
- mdl_json = json.loads(mdl_path.read_text()) + try: + mdl_json = json.loads(mdl_path.read_text()) + except json.JSONDecodeError as e: + typer.echo(f"Error: Invalid JSON in {mdl_path}: {e}", err=True) + raise typer.Exit(1) files = convert_mdl_to_project(mdl_json)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wren/src/wren/context_cli.py` around lines 59 - 65, Wrap the JSON parsing call so malformed MDL yields a friendly error: surround the json.loads(mdl_path.read_text()) with a try/except that catches json.JSONDecodeError, call typer.echo with a clear message including the mdl_path (and optionally the JSON error message) to stderr, and then raise typer.Exit(1) instead of letting the raw traceback propagate; keep the subsequent convert_mdl_to_project and write_project_files(project_path, force=force) flow unchanged so only parsing errors are handled here.wren/src/wren/context.py (1)
112-114: Consider defensive handling for missing modelname.If a model in the input MDL lacks a
namefield, line 114 raises an unhandledKeyError. Since this is a user-facing import tool, a clearer error message would improve UX.💡 Optional: Add validation for missing name
for model in mdl_json.get("models", []): model_snake = _convert_keys_to_snake(model) - name = model_snake["name"] + name = model_snake.get("name") + if not name: + raise ValueError(f"Model missing required 'name' field: {model_snake}") dir_path = f"models/{name}"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wren/src/wren/context.py` around lines 112 - 114, The loop over mdl_json.get("models", []) accesses model_snake["name"] directly which raises KeyError for missing model names; in the block that iterates models (where mdl_json, _convert_keys_to_snake and model_snake are used) add a defensive check for the "name" key (or use model_snake.get("name")) and if missing raise a clear, user-facing error (e.g., ValueError) or log a descriptive message including the model payload or index and skip processing, so import failures surface with an actionable message rather than a raw KeyError.wren/tests/unit/test_convert_mdl.py (1)
309-323: Consider extracting CliRunner setup to a fixture.The
CliRunnerimport and instantiation is repeated in each CLI test. A shared fixture would reduce duplication.♻️ Optional: Extract runner fixture
Add a fixture at the module level:
`@pytest.fixture` def runner(): from typer.testing import CliRunner return CliRunner()Then use it in tests:
-def test_cli_init_from_mdl(tmp_path: Path, sample_mdl_file: Path): - from typer.testing import CliRunner - - from wren.cli import app - - runner = CliRunner() +def test_cli_init_from_mdl(tmp_path: Path, sample_mdl_file: Path, runner): + from wren.cli import app + project_dir = tmp_path / "project" result = runner.invoke(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wren/tests/unit/test_convert_mdl.py` around lines 309 - 323, Extract the repeated CliRunner setup into a pytest fixture and update tests to use it: create a module-level fixture named e.g. runner that imports and returns typer.testing.CliRunner(), then update test functions like test_cli_init_from_mdl to accept the runner fixture instead of creating a new CliRunner instance inside the test; ensure references in the test use the injected runner when invoking app (from wren.cli import app) and remove the in-test import/instantiation to eliminate duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@wren/src/wren/context_cli.py`:
- Around line 59-65: Wrap the JSON parsing call so malformed MDL yields a
friendly error: surround the json.loads(mdl_path.read_text()) with a try/except
that catches json.JSONDecodeError, call typer.echo with a clear message
including the mdl_path (and optionally the JSON error message) to stderr, and
then raise typer.Exit(1) instead of letting the raw traceback propagate; keep
the subsequent convert_mdl_to_project and write_project_files(project_path,
force=force) flow unchanged so only parsing errors are handled here.
In `@wren/src/wren/context.py`:
- Around line 112-114: The loop over mdl_json.get("models", []) accesses
model_snake["name"] directly which raises KeyError for missing model names; in
the block that iterates models (where mdl_json, _convert_keys_to_snake and
model_snake are used) add a defensive check for the "name" key (or use
model_snake.get("name")) and if missing raise a clear, user-facing error (e.g.,
ValueError) or log a descriptive message including the model payload or index
and skip processing, so import failures surface with an actionable message
rather than a raw KeyError.
In `@wren/tests/unit/test_convert_mdl.py`:
- Around line 309-323: Extract the repeated CliRunner setup into a pytest
fixture and update tests to use it: create a module-level fixture named e.g.
runner that imports and returns typer.testing.CliRunner(), then update test
functions like test_cli_init_from_mdl to accept the runner fixture instead of
creating a new CliRunner instance inside the test; ensure references in the test
use the injected runner when invoking app (from wren.cli import app) and remove
the in-test import/instantiation to eliminate duplication.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f366aa19-5fc6-4e45-aff0-fb26d1d3304a
⛔ Files ignored due to path filters (1)
wren/uv.lockis excluded by!**/*.lock
📒 Files selected for processing (3)
wren/src/wren/context.pywren/src/wren/context_cli.pywren/tests/unit/test_convert_mdl.py
Summary
wren context initwith--from-mdl <path>to convert a legacy camelCase MDL JSON into a v2 YAML project in one command--forceflag to allow overwriting an existing projectcontext buildChanges
wren/src/wren/context.py_camel_to_snake()— known mapping table + generic regex fallback_convert_keys_to_snake()— recursive key conversion (inverse of_convert_keys)ProjectFiledataclass —relative_path+contentconvert_mdl_to_project()— MDL JSON → list ofProjectFile(pure function, testable)write_project_files()— writes files to disk with--forceguardwren/src/wren/context_cli.pyinitextended:--from-mdl(optional path) and--force(bool)--from-mdl: existing scaffold behavior unchanged--from-mdl: loads JSON, converts, writes, prints next-steps hintwren/tests/unit/test_convert_mdl.py(new, 25 tests)_camel_to_snakeparametric tests + round-trip guarantee for all known keyswrite_project_filesguard tests (refuse overwrite, force overwrite)convert → buildround-trip testtyper.testing.CliRunnerTest plan
uv run pytest tests/unit/test_convert_mdl.py— 25/25 passedjust lint— clean🤖 Generated with Claude Code
Summary by CodeRabbit
wren context init --from-mdl--forceflag to overwrite existing project configurations during initialization