"""m2-market CLI entrypoint (contracts/cli.md).""" from __future__ import annotations import sys import click from m2_market import __version__ from m2_market.config import ConfigError, load_config # Exit-code map (contracts/cli.md). EXIT_OK = 0 # ok / no-op EXIT_USAGE = 1 # usage (also: missing/invalid config) EXIT_NOT_FOUND = 2 # not found EXIT_INSUFFICIENT_FUNDS = 3 # insufficient funds EXIT_APPLY_FAILED = 4 # apply failed (refunded) EXIT_VALIDATION = 5 # validation / firewall rejection EXIT_RAILS_NOT_LANDED = 6 # rails-not-landed (m2core_sync stub invoked) @click.group(name="m2-market") @click.option("--json", "json_output", is_flag=True, default=False, help="Emit machine-readable JSON output.") @click.version_option(version=__version__, prog_name="m2-market") @click.pass_context def main(ctx: click.Context, json_output: bool) -> None: """M2 Marketplace CLI (search | show | install | wallet | publish | reindex).""" ctx.ensure_object(dict) ctx.obj["json"] = json_output try: ctx.obj["config"] = load_config() except ConfigError as exc: click.echo(f"m2-market: config error: {exc}", err=True) ctx.exit(EXIT_USAGE) def _not_implemented(ctx: click.Context, command: str) -> None: click.echo(f"m2-market {command}: not implemented", err=True) ctx.exit(EXIT_USAGE) @main.command() @click.argument("query") @click.option("--type", "listing_type", default=None, help="Filter by listing type (e.g. solution).") @click.option("--limit", type=int, default=None, help="Maximum number of results.") @click.pass_context def search(ctx: click.Context, query: str, listing_type: str | None, limit: int | None) -> None: """Semantic catalog query, tenant-filtered.""" _not_implemented(ctx, "search") @main.command() @click.argument("listing_id") @click.pass_context def show(ctx: click.Context, listing_id: str) -> None: """Full listing detail: evidence, price, permissions diff, seller, version.""" _not_implemented(ctx, "show") @main.command() @click.argument("listing_id") @click.option("--yes", is_flag=True, default=False, help="Skip interactive confirmation.") @click.pass_context def install(ctx: click.Context, listing_id: str, yes: bool) -> None: """Resolve, pay, fetch, apply, and record a listing install.""" _not_implemented(ctx, "install") @main.command() @click.option("--operator", "operator", default=None, help="Operator id to query (defaults to config).") @click.pass_context def wallet(ctx: click.Context, operator: str | None) -> None: """Balance + recent transactions from the ledger.""" _not_implemented(ctx, "wallet") @main.command() @click.argument("bundle_dir") @click.pass_context def publish(ctx: click.Context, bundle_dir: str) -> None: """Validate a bundle and open a listing PR on the registry.""" _not_implemented(ctx, "publish") @main.command() @click.pass_context def reindex(ctx: click.Context) -> None: """Invoke the catalog indexer full rebuild (admin).""" _not_implemented(ctx, "reindex") if __name__ == "__main__": sys.exit(main())