# Contract: m2-market CLI (v1) Python (uv) package `m2-market`; installed on desktops + host. Config `~/.m2-market/config.toml`: `operator_id`, `ledger_url` + key, `memory_api_url` + key, `forgejo_url` + token, `tenant`. State: `~/.m2-market/state.json` (data-model.md InstallState). All commands support `--json` for machine consumption (the Store shells these). ## Commands ### `m2-market search [--type solution] [--limit N]` Semantic catalog query (contracts/catalog-index.md), tenant-filtered. Output: table (or JSON): listing_id, name, summary, price, installs, rating. ### `m2-market show ` Full listing detail: evidence summary, price, permissions diff (from solution.permissions vs current state), seller, version, install_ref. ### `m2-market install [--yes]` The FR-005 sequence, in order: 1. Resolve listing from catalog; confirm price + permissions diff (interactive unless `--yes`). 2. `POST /tx/install` to ledger (ref = `listing_id:machine:uuid`) → grant. On 402: exit 3 "insufficient funds", nothing applied (Story 1 AC-2). 3. Fetch bundle release from registry; verify `content_hash`. 4. `AdapterProtocol.plan → apply → verify` (contracts/apply-adapter.md). On apply/verify failure: `POST /tx/refund {ref}`, exit 4 with remediation text (Story 4 AC-3 semantics). 5. Record InstallState; print entrypoint invocation hint (Story 1 AC-4). Re-run with existing grant + applied state → idempotent no-op, exit 0 (Story 1 AC-3). ### `m2-market wallet [--operator X]` Balance + recent tx from ledger. ### `m2-market publish ` (builder-side, v1 minimal) Validate against schemas → push branch + open listing PR on the registry (Story 2 step 1–2). Does NOT merge; humans/veto do. ### `m2-market reindex` (admin) Invokes indexer full rebuild (constitution II operational check). ## Exit codes 0 ok/no-op · 1 usage · 2 not found · 3 insufficient funds · 4 apply failed (refunded) · 5 validation/firewall rejection · 6 rails-not-landed (m2core_sync stub invoked)