Refactor: extract shared action layer for CLI and MCP tools#272
Draft
ryanjoneil wants to merge 72 commits into
Draft
Refactor: extract shared action layer for CLI and MCP tools#272ryanjoneil wants to merge 72 commits into
ryanjoneil wants to merge 72 commits into
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create nextmv/cli/actions/app.py with pure action functions wrapping SDK calls (list_apps, create_app, get_app, delete_app, app_exists, update_app, push_app). Add tests in tests/cli/actions/test_app.py. Update all 7 CLI app commands and the MCP tools file to delegate through the new action functions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create cli/actions/app.py with 7 pure action functions - Add tests/cli/actions/test_app.py with 11 unit tests - Update CLI cloud/app/ commands to import from actions - Update MCP tools/app.py to import from actions - Fix 2 existing test patches for new import paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves shared instance business logic out of CLI commands and MCP tools into pure functions in nextmv/cli/actions/instance.py, following the same pattern established for app and version actions. - Add nextmv/cli/actions/instance.py with list/get/create/update/delete functions - Add tests/cli/actions/test_instance.py with full unittest coverage - Update all instance CLI commands to use Client(profile=) + action calls - Update MCP tools/instance.py to import and delegate to action functions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t mocks - Add nextmv/cli/actions/switchback.py with pure action functions - Add tests/cli/actions/test_switchback.py with full test coverage - Update all CLI cloud/switchback/* commands to use Client directly - Update MCP tools/switchback.py to import from actions - Fix tests/cli/test_mcp.py: update 10 tests from _get_app mock pattern to _get_client + actions.Application patch pattern - Improve ensemble action error messages to include index in KeyError/ ValueError for run_groups and rules validation failures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The refactor accidentally dropped the --manifest option, verbose=True, and rich_print=True from the CLI push command. The action function push_app() is still used by MCP (which doesn't need these), but the CLI push now calls the SDK directly to preserve its full behavior. Also fixes unused imports flagged by ruff. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Revert unrelated removal of local_only field from ManifestOption (breaking SDK change that doesn't belong in this refactor) - Wire scenario/create.py and ensemble/create.py through action functions instead of calling SDK directly - Add missing return type annotations to 8 functions in actions/run.py and actions/local.py - Remove unused validate_content_format import from MCP _helpers.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Plan and design docs belong in the agents repo memory system, not in the nextmv-py source tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cloud run data was already cached to ~/.nextmv/runs/ with a predictable directory structure. Experiment data (scenario, batch, ensemble, shadow, switchback, acceptance) was written to /tmp/ with random names that get lost across sessions. Now experiments save to ~/.nextmv/experiments/<endpoint>/<type>/<id>/, mirroring the run cache layout. Batch metadata uses metadata.json, ensemble run results use run_result.json, everything else uses result.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 tasks
Design for eliminating metadata duplication between Typer CLI commands and FastMCP tool registrations by making the actions layer the single source of truth for parameter signatures, types, help text, and docstrings. Connectors become thin wiring tables built via introspection-based framework helpers. Pilot scope: `app` domain only, local only (no push/PR). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add "Design Principle: Structured Data → Rendered Markup" section naming the pattern used by examples, on_success, result_message, and the save-message formatting. - Add `saved_noun=` parameter to `cli.command()` and specify the save-message format, eliminating ~30 hand-written save-success strings across the full CLI when migration completes. - Switch `examples=` from a hand-formatted docstring block to structured tuples of (description, command) pairs, rendered by the framework. - Clarify framework module layout: `cli.progress`, `mcp_fw.client`, and `mcp_fw.clean` are re-exports of existing helpers. - Document wrapper signature forwarding preserves parameter kinds (positional-only, keyword-only). - Clarify that CLI-only option aliases (e.g. OutputOption) are framework-internal and never imported by domain files. - Clarify `normalize_empty=` (declarative) vs `mcp_fw.clean()` (imperative) usage guidance. - Add caveat that tests asserting on implementation details will need rewriting as observable-contract assertions. - Add test cases for `saved_noun` and preserved parameter kinds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show inline [magenta]...[/magenta] highlighting in LIST_EXAMPLES and CREATE_EXAMPLES tuples, matching the hand-written blocks on develop verbatim. - Document that _render_examples passes description markup through unchanged and only adds surrounding structure (header, bullets, $ [dim]...[/dim] wrapper). - Add snapshot-comparison test against current help output for at least one example-rich command and one simple command to confirm byte-for-byte fidelity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extracts the interactive push-and-version-and-instance workflow from the current hand-written push.py command into a new module that takes client: Client as its first parameter so it can be wired via cli.command(handles_own_output=True) in Task 11. The thin push_app action continues to serve the MCP side unchanged. Preserves the exact inline Typer annotations (rich_help_panel groupings, short flags, metavars, envvars, help text) from the current push.py. Helper functions (handle_push, _handle_version_creation, etc.) copied verbatim.
Replaces 7 per-command Typer files with a single connector table in cli/cloud/app/__init__.py that registers each command via cli.command(). The 6 thin commands (list, create, get, delete, exists, update) wrap pure actions from cli.actions.app directly. The rich push command wraps run_push_workflow from cli.cloud.app._workflows via handles_own_output=True, which preserves the full interactive version-and-instance flow while routing through the framework. Also updates cli/init.py to import handle_push from _workflows (its new home) and trims the duplicate Examples block from run_push_workflow's docstring since cli.command(examples=...) now renders it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refreshes the golden help fixtures captured pre-refactor in Task 10 to their post-refactor state, and adds a two-layer test: 1. TestHelpOutputSnapshots — byte-for-byte comparison (after whitespace normalization and ANSI stripping) against the refreshed fixtures. Locks in forward compatibility: future accidental changes to the framework's help rendering will be caught here. 2. TestHelpOutputStructure — presence checks for flag names, short forms, envvar hints, example blocks, and rich_help_panel groupings. These don't depend on exact byte ordering and survive cosmetic Typer rendering changes. Post-refactor help differs from the pre-refactor hand-written state in three intentional ways, all documented in the Task 11 commit: expanded multi-paragraph action docstrings, option ordering following the action signature, and generic --output help text from the framework-internal _OutputOption alias. Fixtures are captured via Typer's CliRunner (the same harness the snapshot tests use) so program-name and terminal-width rendering are consistent between capture and verification.
Replace the seven hand-written @mcp.tool() wrappers in nextmv/cli/mcp/tools/app.py with mcp_fw.tool() calls. Each tool's parameter signature, types, and descriptions are now derived from the underlying action in nextmv.cli.actions.app; only per-tool wiring (name, empty-string normalization, result-message formatting) lives in app.py. Schema changes (existing clients are unaffected): - cloud_create_app now treats `name` as optional (was required in the old hand-written wrapper). The underlying create_app action has always accepted name=None — the "required" was enforced only at the MCP tool boundary. Clients that omit `name` no longer see a validation error at the MCP layer. - cloud_update_app now exposes `default_experiment_instance` as an optional parameter (was not exposed in the old wrapper). This is a schema extension, not a break. Also update test_cloud_list_apps_calls_sdk to patch _get_client at its new lookup site (nextmv.cli.mcp.framework.tool) since the framework imports the helper at module load time. This is mock-coupling to the old inline-wrapper internals; the observable behavior is unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move Callable imports from typing to collections.abc (UP035). - Sort imports in test files (I001). - Remove unused Annotated import from test_options.py (F401).
…tory Addresses ruff C901 — command() was McCabe complexity 11. Extracting the inner wrapper closure into a module-level _build_wrapper factory separates signature/metadata fixup (command's job) from runtime dispatch (the wrapper's job). No behavior change. Also removes an unused mock_client variable in test_command.py (F841).
- actions/version.py: use dual-purpose aliases, multi-paragraph docstrings, add version_exists action (previously only in the CLI, not MCP). - cloud/version/__init__.py: rewrite as connector table; delete 6 per-command files (create, delete, exists, get, list, update). - cloud/version/_workflows.py: new file with run_version_exists_check, which wraps the thin version_exists action with CLI-specific JSON print + exit code 1 on missing version. - mcp/tools/version.py: rewrite as mcp_fw.tool() calls. Adds new cloud_version_exists tool (previously only the CLI had it). - tests/cli/mcp/test_tools.py: bump expected tool count 89 -> 90 for the new cloud_version_exists tool. One intentional UX regression: the "creating or getting" progress message variant on --exist-ok is gone; all creates now show "Creating version...". Rationale: cli.command() supports only static progress strings, and the marginal text difference doesn't justify a framework extension.
- actions/instance.py: use dual-purpose aliases, multi-paragraph docstrings, add instance_exists action (previously only in the CLI, not MCP). Add exist_ok to create_instance and locked to update_instance to expose the full SDK surface. - cloud/instance/__init__.py: rewrite as connector table; delete 6 per-command files (create, delete, exists, get, list, update). - cloud/instance/_workflows.py: new file with build_options/build_config helpers (moved verbatim from the old create.py), run_create_instance, run_update_instance, and run_instance_exists_check workflows. The create and update workflows build an InstanceConfiguration from many individual flags before delegating to the thin actions; the exists workflow wraps instance_exists with a JSON print + exit code 1 on a missing instance. Update preserves --output/-u (instead of -o) so the short flag does not collide with --options/-o. - mcp/tools/instance.py: rewrite as mcp_fw.tool() calls. Adds new cloud_instance_exists tool (previously only the CLI had it). - tests/cli/actions/test_instance.py: update assertions to include the new exist_ok and locked kwargs threaded through to the SDK. - tests/cli/mcp/test_tools.py: bump expected tool count 90 -> 91 for the new cloud_instance_exists tool. Two intentional UX regressions, mirroring the version migration: - The "Creating or getting instance..." progress variant on --exist-ok is gone; create always shows "Creating instance...". - The delete command no longer takes --yes / shows a confirmation prompt, consistent with how app delete and version delete were migrated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces cli.DeleteConfirmation (a NamedTuple with confirm/decline/
succeeded templates) and a delete_confirm= kwarg on cli.command(). When
set, the generated command injects a --yes/-y flag and, unless --yes is
supplied, prompts the user via confirmation() before calling the action.
Declining prints the decline message and returns; accepting calls the
action and prints the success message.
Fixes a regression introduced during the app (pilot) and instance
migrations where the interactive --yes confirmation on delete commands
was silently dropped. Destructive operations should not run without
either explicit --yes or an interactive prompt.
- cli/framework/command.py: DeleteConfirmation NamedTuple, delete_confirm
kwarg, wrapper injection of yes: YesOption param, runtime confirmation
gate before the action call, success message after.
- cli/framework/options.py: YesOption dual-purpose alias.
- cli/framework/__init__.py: re-export DeleteConfirmation and YesOption.
- cli/cloud/{app,version,instance}/__init__.py: switch delete from
on_success= to delete_confirm=, restoring the confirmation prompt.
- tests/cli/framework/test_command.py: 7 new tests in TestDeleteConfirm
covering incompatibility checks, --yes bypass, accept/decline flows,
and help-output flag injection.
- actions/secrets.py: dual-purpose aliases, multi-paragraph docstrings,
add update_secrets_collection action (previously CLI-only).
- cloud/secrets/__init__.py: connector table. list/get/delete wrap the
thin actions directly; create/update route through _workflows.py
because they parse the repeatable --secrets JSON flag.
- cloud/secrets/_workflows.py: build_secrets (JSON->Secret parser),
run_create_secrets_collection, run_update_secrets_collection.
- mcp/tools/secrets.py: mcp_fw.tool() calls. Adds new
cloud_update_secrets_collection tool (previously CLI-only).
- delete uses delete_confirm for interactive confirmation + --yes.
- tests/cli/mcp/test_tools.py: tool count 91 -> 92.
Deleted: cloud/secrets/{create,delete,get,list,update}.py.
- actions/input_set.py: dual-purpose aliases, multi-paragraph docstrings, add start_time/end_time parameters to create_input_set for time-range filtering. update_input_set gains managed_input_ids pass-through. - cloud/input_set/__init__.py: connector table. list/get/delete wrap thin actions; create/update route through _workflows.py because they parse --managed-inputs JSON and (for create) RFC 3339 time flags. - cloud/input_set/_workflows.py: _parse_managed_inputs helper, run_create_input_set, run_update_input_set. Create preserves the safe_id fallback for auto-generated IDs. - mcp/tools/input_set.py: mostly mcp_fw.tool() calls. cloud_create_input_set kept as an inline @mcp.tool() to preserve its "exactly one method" validation that returns error strings instead of raising. - tests/cli/actions/test_input_set.py: update call kwargs for the new start_time/end_time/inputs parameters.
…iases - actions/managed_input.py: dual-purpose aliases, multi-paragraph docstrings, add update_managed_input action (previously CLI-only). - cloud/managed_input/__init__.py: connector table. list/get/delete wrap the thin actions; create/update route through _workflows.py for the content-format Format object, precondition check, and --output save. - cloud/managed_input/_workflows.py: run_create_managed_input (preserves the --content-format -> Format translation and upload/run precheck), run_update_managed_input (precondition check + --output file save). - mcp/tools/managed_input.py: mcp_fw.tool() calls. create stays as an inline @mcp.tool() because it takes an untyped `input` dict that the framework builder cannot introspect cleanly. Adds new cloud_update_managed_input tool. - tests/cli/mcp/test_tools.py: tool count 92 -> 93.
- actions/acceptance.py: dual-purpose aliases, multi-paragraph docstrings, add update_acceptance_test action (previously CLI-only). - cloud/acceptance/__init__.py: connector table. list/delete wrap thin actions; get/create/update route through _workflows.py for polling, JSON metric parsing, and --output file saves. - cloud/acceptance/_workflows.py: build_metrics (JSON->Metric parser), run_get_acceptance_test (with --wait polling), run_create_acceptance_test (inline rich_help_panel groupings + polling + multi-line docstring attached via __doc__ since typer docstrings can't include f-strings), run_update_acceptance_test. - mcp/tools/acceptance.py: mcp_fw.tool() calls. get stays as inline @mcp.tool() because it saves the result to an experiment cache file rather than returning it raw. Adds cloud_update_acceptance_test. - tests/cli/mcp/test_tools.py: tool count 93 -> 94.
- actions/scenario.py: dual-purpose aliases, multi-paragraph docstrings, add update_scenario_test action (previously CLI-only). build_scenario helper preserved as an internal dict->Scenario converter. - cloud/scenario/__init__.py: connector table. list/delete wrap thin actions; get/metadata/create/update route through _workflows.py for polling, JSON scenario parsing, --output file saves, and the separate scenario_test_metadata SDK path. - cloud/scenario/_workflows.py: build_scenario_dicts (JSON parser), run_get_scenario_test (with --wait polling), run_get_scenario_metadata (metadata-only retrieval), run_create_scenario_test (inline rich_help_panel groupings + polling + multi-line docstring attached via __doc__ since typer docstrings can't include f-strings), run_update_scenario_test. - mcp/tools/scenario.py: mcp_fw.tool() calls for list/update/delete. get stays as inline @mcp.tool() because it saves the result to an experiment cache file; create stays inline to catch validation errors and return user-friendly strings. Adds cloud_update_scenario_test. - tests/cli/mcp/test_tools.py: tool count 94 -> 95. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/ensemble.py: dual-purpose aliases, multi-paragraph docstrings, add update_ensemble action (previously CLI-only). Rename create_ensemble and related params from ensemble_id to ensemble_definition_id to match the option alias. parse_evaluation_rule / build_ensemble_run_config / ensemble_run_with_result / ensemble_run_submit helpers preserved — they power the bespoke run-submission MCP tools. - cloud/ensemble/__init__.py: connector table. list/get/delete wrap thin actions; create/update route through _workflows.py for repeatable JSON --run-groups / --rules parsing and --output file save. - cloud/ensemble/_workflows.py: build_run_group_dicts / build_rule_dicts (JSON validators), run_create_ensemble (inline rich_help_panel + multi-line docstring attached via __doc__ since typer docstrings can't include f-string enum interpolation), run_update_ensemble. - mcp/tools/ensemble.py: mcp_fw.tool() calls for list/get/update/delete. create stays as inline @mcp.tool() to catch validation errors and return user-friendly strings. The run-submission tools (cloud_ensemble_run / cloud_ensemble_run_submit) stay inline because of bespoke input_dir_path / managed_input_id handling. register() is split into two helpers to stay under the C901 complexity limit. Adds cloud_update_ensemble. - tests/cli/actions/test_ensemble.py: update kwarg name to match the renamed ensemble_definition_id parameter. - tests/cli/mcp/test_tools.py: tool count 95 -> 96. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/batch.py: dual-purpose aliases, multi-paragraph docstrings, add update_batch action (previously CLI-only) and runs passthrough on create_batch. - cloud/batch/__init__.py: connector table. list/delete wrap thin actions; get/metadata/create/update route through _workflows.py for polling, JSON runs/option_sets parsing, --output file saves, and the separate metadata retrieval path. - cloud/batch/_workflows.py: build_option_sets and build_runs JSON parsers, run_get_batch (with --wait polling), run_get_batch_metadata (metadata-only retrieval), run_create_batch (inline rich_help_panel groupings + polling + multi-line docstring attached via __doc__ since typer docstrings can't include f-string-style content), run_update_batch. - mcp/tools/batch.py: mcp_fw.tool() calls for list/create/update/delete. get and metadata stay as inline @mcp.tool() because they save results to the experiment cache. Adds cloud_update_batch. - tests/cli/actions/test_batch.py: add runs=None kwarg to the two create_batch assertion matchers since the action now passes runs through to the SDK. - tests/cli/mcp/test_tools.py: tool count 96 -> 97. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/shadow.py: dual-purpose aliases, multi-paragraph docstrings, add shadow_test_metadata and update_shadow_test actions (previously CLI-only). - cloud/shadow/__init__.py: connector table. list/start/delete wrap thin actions; get/metadata/create/update/stop route through _workflows.py for JSON comparison parsing, SDK start/termination dataclass construction, --output file saves, the separate metadata retrieval path, and the --intent enum for the stop flow. - cloud/shadow/_workflows.py: run_get_shadow_test (with --output), run_get_shadow_metadata (metadata-only retrieval), run_create_shadow_test (inline rich_help_panel groupings + datetime parsing + multi-line docstring attached via __doc__), run_update_shadow_test, run_stop_shadow_test (exposes the StopIntent enum as --intent). - mcp/tools/shadow.py: mcp_fw.tool() calls for list/update/start/stop/delete. get and metadata stay as inline @mcp.tool() because they save results to the experiment cache; create stays inline because its termination_events/start_events arguments are untyped dicts the framework can't introspect and it benefits from catching bad LLM payloads with a friendly error string. Adds cloud_shadow_test_metadata and cloud_update_shadow_test. - tests/cli/mcp/test_tools.py: tool count 97 -> 99. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/switchback.py: dual-purpose aliases, multi-paragraph docstrings, add switchback_test_metadata and update_switchback_test actions (previously CLI-only). create_switchback_test now threads the optional ``start`` datetime through to the SDK. - cloud/switchback/__init__.py: connector table. list/start/delete wrap thin actions; get/metadata/create/update/stop route through _workflows.py for --output file saves, inline comparison/duration/start flags that compose into a single SDK create call, the separate metadata retrieval path, and the --intent enum for the stop flow. - cloud/switchback/_workflows.py: run_get_switchback_test (with --output), run_get_switchback_metadata (metadata-only retrieval), run_create_switchback_test (inline rich_help_panel groupings and datetime parsing), run_update_switchback_test, run_stop_switchback_test (exposes the StopIntent enum as --intent). - mcp/tools/switchback.py: mcp_fw.tool() calls for list/create/update/start/stop/delete. get and metadata stay as inline @mcp.tool() because they save results to the experiment cache. Adds cloud_switchback_test_metadata and cloud_update_switchback_test. - tests/cli/actions/test_switchback.py: add start=None kwarg to the two create_switchback_test assertion matchers since the action now passes start through to the SDK. - tests/cli/mcp/test_tools.py: tool count 99 -> 101. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/account.py: dual-purpose aliases, multi-paragraph docstrings, add create_account, update_account, delete_account (previously CLI-only via build_account + Account.new/.update/.delete). - cloud/account/__init__.py: connector table. get/delete wrap thin actions; create/update route through _workflows.py for admin email parsing and --output file saves. - cloud/account/_workflows.py: _parse_admins (splits comma-separated flags), run_create_account, run_update_account. - mcp/tools/account.py: mcp_fw.tool() calls. Adds cloud_create_account, cloud_update_account, cloud_delete_account. - tests/cli/mcp/test_tools.py: tool count 101 -> 104.
- actions/sso.py: expand from just delete_domain to the full set of SSO
operations (get/create/update/enable/disable/delete_configuration +
delete_domain). All wrap SSOConfiguration SDK methods with
multi-paragraph docstrings.
- cloud/sso/__init__.py: connector table with a nested `domain`
subcommand. All commands use handles_own_output=True because they
need stdin fallbacks (create), confirmation prompts
(enable/disable/delete/domain-delete), or plain success messages
with no data return.
- cloud/sso/_workflows.py: run_get/create/update/enable/disable/delete
_sso_configuration + run_delete_domain. Preserves stdin fallback on
create, confirmation gates on destructive ops.
- mcp/tools/sso.py: mcp_fw.tool() calls. Adds cloud_get_sso_configuration
as a read-only surface. Does NOT expose
create/update/enable/disable/delete via MCP — destructive org-wide
operations should not be driven by LLMs without confirmation.
- tests/cli/mcp/test_tools.py: tool count 104 -> 105.
Deleted: cloud/sso/{create,delete,disable,enable,get,update}.py and
cloud/sso/domain/ entire directory.
- actions/community.py: multi-paragraph docstrings; no parameter changes
(community has no cloud-entity IDs to port to dual-purpose aliases).
- community/__init__.py: connector table with handles_own_output=True for
both list and clone. list renders Rich tables; clone calls the SDK's
verbose+rich cloning path directly.
- community/_workflows.py: run_list_community_apps, run_clone_community_app,
plus the Rich table/list helpers moved from the old list.py (byte-for-byte
preserved: _apps_table, _apps_list, _versions_table, _versions_list,
_find_app).
- mcp/tools/community.py: list migrated to mcp_fw.tool(); clone stays
inline because it has a bespoke return string.
Deleted: community/{clone,list}.py.
Collapses the nine per-command files under cloud/run/ into a single connector __init__.py driven by cli.command() and a _workflows.py module that houses the complex flows (content-format-aware save, stdin input parsing, polling, tracked-run file parsing). Actions are refactored to take client: Client + app_id as their first parameters so they work with the framework's first-param assertion. Three simple MCP tools (cloud_run_status, cloud_list_runs, cloud_cancel_run) are migrated to mcp_fw.tool(); the rest remain inline because they cache run artifacts on disk. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces nextmv.cli.actions.marketplace as the first action file for
a CLI-only domain (marketplace is not exposed via MCP). Collapses the
twelve per-command files under cloud/marketplace/{app,subscription,
version}/ into three connector __init__.py files driven by
cli.command().
Adds dual-purpose aliases for the marketplace-specific option metadata
(partner ID, subscription ID, version ID, app ID) so the actions use
the same shared-connectors pattern even though no MCP tools consume
them today.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- actions/upload.py (new): create_upload_url thin action. - actions/data.py (new): upload_data thin action. - cloud/upload/__init__.py: connector table (single command, thin). - cloud/data/__init__.py: connector table. upload routes through _workflows.py for stdin/file/dir/.tar.gz input resolution. - cloud/data/_workflows.py: run_upload_data + _resolve_data_kwarg. - No MCP tools added — these domains aren't exposed via MCP. Deleted: cloud/upload/create.py, cloud/data/upload.py.
| ) | ||
|
|
||
| if output is not None and output != "": | ||
| Path(output).write_text(json.dumps(collection_dict, indent=2)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cli/actions/module (18 action modules, ~60 CLI files updated, 18 MCP tool files slimmed)test_mcp.py(2163 lines) into 9 domain-specific test files undertests/cli/mcp/tests/cli/actions/~/.nextmv/experiments/instead of/tmp/(mirrors existing run cache pattern)local_onlyfield, wire scenario/ensemble create through actions, add return type annotationsTest plan
~/.nextmv/experiments/<endpoint>/<type>/<id>/🤖 Generated with Claude Code