@deeeed/metamask-harness 0.15.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ <!-- Generated by scripts/generate-cli-ergonomics-audit.mjs. Do not edit by hand. -->
2
+ # CLI ergonomics audit
3
+
4
+ This matrix tracks the public command surface. “Covered” means the shared preflight contract and
5
+ `tests/contract/cli-teaching-errors.test.sh` exercise the behavior without dispatching runtime work.
6
+ Runtime recovery is claimed only where command-specific failures return a stable `userAction`.
7
+ Success hints are intentionally limited to lifecycle transitions with one truthful next command;
8
+ discovery, read-only, and terminal evidence commands do not invent one. ID prefix support is N/A
9
+ unless a command displays a shortened ID that another command accepts.
10
+
11
+ | Command | Bad flag | First feedback | `--json` recovery | Success next step | Displayed ID prefix |
12
+ |---|---|---|---|---|---|
13
+ | `status` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | structured `next` | N/A: command displays no shortened ID |
14
+ | `checklist` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage errors covered; no runtime recovery claim | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
15
+ | `actions` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
16
+ | `stop` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | launch `next` when the adapter has one unambiguous launch | N/A: command displays no shortened ID |
17
+ | `call` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
18
+ | `flows` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage errors covered; no runtime recovery claim | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
19
+ | `run` | covered: `CLI_UNKNOWN_OPTION` | TTY intent before slow work; machine output clean | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
20
+ | `last` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
21
+ | `doctor` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
22
+ | `check` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
23
+ | `recipe-quality` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
24
+ | `provision` | covered: `CLI_UNKNOWN_OPTION` | TTY intent before slow work; machine output clean | usage + runtime `error.code/message/userAction` | launch `next` after install | N/A: command displays no shortened ID |
25
+ | `install` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | verify `next` | N/A: command displays no shortened ID |
26
+ | `verify` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | status `next` | N/A: command displays no shortened ID |
27
+ | `cleanup` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | install `next` | N/A: command displays no shortened ID |
28
+ | `launch` | covered: `CLI_UNKNOWN_OPTION` | TTY intent before slow work; machine output clean | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
29
+ | `logs` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
30
+ | `debug` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage errors covered; no runtime recovery claim | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
31
+ | `update` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
32
+ | `fixtures` | covered: `CLI_UNKNOWN_OPTION` | N/A: no newly introduced slow path | usage + runtime `error.code/message/userAction` | N/A: discovery, read-only, or terminal result | N/A: command displays no shortened ID |
@@ -0,0 +1,104 @@
1
+ # CLI ergonomics human QA
2
+
3
+ Use this short pass after installing a release candidate. Automated contracts remain the release gate; this checklist confirms the experience in a real shell and checkout.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ export TARGET=/absolute/path/to/a/metamask-checkout
9
+ export ADAPTER=extension # extension, mobile, or core
10
+ mm-harness --version
11
+ mm-harness doctor --adapter "$ADAPTER" --target "$TARGET" --json | jq .status
12
+ ```
13
+
14
+ Expected: the intended version is printed and `doctor` returns parseable JSON. Resolve required doctor failures before continuing.
15
+
16
+ ## 1. Teaching errors
17
+
18
+ ```bash
19
+ out="$(mm-harness actions --categoriez --adapter "$ADAPTER" --json 2>/dev/null || true)"
20
+ jq '.error | {code, message, userAction}' <<<"$out"
21
+ ```
22
+
23
+ Expected: `code` is stable, the message names the bad flag and valid options, and `userAction` suggests the closest valid invocation.
24
+
25
+ ## 2. In-tool discovery
26
+
27
+ ```bash
28
+ mm-harness actions --adapter "$ADAPTER" --categories
29
+ mm-harness actions status --adapter "$ADAPTER"
30
+ mm-harness actions --action app.status --adapter "$ADAPTER" --json | jq '{actions, relatedActions}'
31
+ ```
32
+
33
+ Expected: category output is compact, fuzzy search is bounded, and action detail includes related actions when available. A missing example action should produce a teaching error, not silent output.
34
+
35
+ ## 3. Machine-output cleanliness and recovery
36
+
37
+ ```bash
38
+ out="$(mm-harness call definitely.not.an.action --adapter "$ADAPTER" --target "$TARGET" --json 2>/dev/null || true)"
39
+ jq '{status, error}' <<<"$out"
40
+ ```
41
+
42
+ Expected: stdout is one parseable JSON document with `error.code`, `error.message`, and `error.userAction`; it contains no spinner, banner, or human-only glyph.
43
+
44
+ ## 4. Immediate human feedback
45
+
46
+ Run the platform-appropriate launch in an interactive terminal:
47
+
48
+ ```bash
49
+ # Extension
50
+ mm-harness launch --adapter extension --target "$TARGET"
51
+
52
+ # Mobile: choose the intended platform
53
+ mm-harness launch ios --target "$TARGET"
54
+ # mm-harness launch android --target "$TARGET"
55
+ ```
56
+
57
+ Expected: intent/progress appears immediately, long work continues to report progress, and the app becomes ready. Redirected or `--json` stdout stays decoration-free.
58
+
59
+ ## 5. Lifecycle next step
60
+
61
+ For Extension:
62
+
63
+ ```bash
64
+ mm-harness stop --adapter extension --target "$TARGET" --json | jq '{status, next}'
65
+ ```
66
+
67
+ Expected: `next` is a target-scoped launch command. Mobile `stop` intentionally omits `next` because Metro does not identify whether iOS or Android should be relaunched.
68
+
69
+ ## 6. Resume after interruption
70
+
71
+ ```bash
72
+ mm-harness last --target "$TARGET" --json | jq '.last | {command, args, verdict, evidencePaths, startedAt, finishedAt}'
73
+ ```
74
+
75
+ Expected: the last operation can be reconstructed without rerunning it, including its arguments, verdict, evidence paths, and timestamps.
76
+
77
+ ## 7. Recipe proof smoke
78
+
79
+ ```bash
80
+ mm-harness run --list --adapter "$ADAPTER" --json | jq '{actions: .actions[0:5], flows: .flows[0:5]}'
81
+ export RECIPE=replace-with-a-safe-recipe-name-or-path
82
+ export ARTIFACTS_DIR="$(mktemp -d)"
83
+ mm-harness run "$RECIPE" --plan --adapter "$ADAPTER" --target "$TARGET" --json | jq '{status, plan}'
84
+ mm-harness run "$RECIPE" --adapter "$ADAPTER" --target "$TARGET" --artifacts-dir "$ARTIFACTS_DIR"
85
+ ```
86
+
87
+ Expected: the plan validates before execution; the final result identifies evidence paths and `mm-harness last --json` records the run.
88
+
89
+ Optional JSONL check:
90
+
91
+ ```bash
92
+ mm-harness run "$RECIPE" --adapter "$ADAPTER" --target "$TARGET" --artifacts-dir "$ARTIFACTS_DIR-stream" --json-stream | jq -c .
93
+ ```
94
+
95
+ Expected: complete JSON objects arrive line by line while the run is active.
96
+
97
+ ## Cleanup
98
+
99
+ ```bash
100
+ [ "$ADAPTER" = core ] || mm-harness stop --adapter "$ADAPTER" --target "$TARGET"
101
+ mm-harness status --target "$TARGET" --json | jq .
102
+ ```
103
+
104
+ Record the harness version, checkout SHA, adapter/platform, failed command, exit code, and JSON envelope for any discrepancy.
package/docs/CLI-SPEC.md CHANGED
@@ -366,6 +366,8 @@ Installs the cached Runway iOS dev client onto a prepared mobile slot. It does n
366
366
 
367
367
  **Validates first (adapter-aware):** Before touching any device, `run` validates the recipe: action existence in the adapter's manifest, platform support for each action, and fixture preconditions. Validation errors exit 5 with a structured error list. `--plan` stops here — prints the plan and exits, no device touched. Without `--plan`, validation failures are fatal before any execution begins.
368
368
 
369
+ **Run-scoped diagnostics:** Mobile combines its bounded in-app issue buffer with application-log bytes appended while the recipe executes; Extension owns a CDP console subscription for the run. The bounded, redacted result is written to `diagnostics.json`, indexed as a diagnostic artifact, and summarized as non-blocking `sideFindings`; it never changes recipe pass/fail and does not claim causality. Core is headless, so this is N/A.
370
+
369
371
  > **Wave-2 status:** adapter-aware validation (`validateRecipeDocument` +
370
372
  > `validateRecipeWithManifest`, shared helper `validateRecipeAdapterAware`) is
371
373
  > wired for **`run --plan`** and **`call`** at the pinned deps (`@farmslot/protocol`
@@ -377,7 +379,7 @@ Installs the cached Runway iOS dev client onto a prepared mobile slot. It does n
377
379
  > recipe) validate fully; the execute path relies on the engine's own manifest
378
380
  > validation. Tagged in code (`runner/src/cli.ts` `validateRecipeAdapterAware`).
379
381
 
380
- **Phase state-machine:** `resolve → installhealthcheckrecovervalidate launch execute` (phase reported in `--json` as `phase`; `--json-stream` emits one event per phase transition).
382
+ **Execution phases:** `resolve → validateinstallhealthcheckexecute`, with `recover` emitted only when bounded recovery actually runs. `--json-stream` emits each transition as it occurs.
381
383
 
382
384
  **Auto-ensure overlay:** `run` checks for the runtime overlay before executing (phase: `install`). If missing: installs it inline with a one-line notice, then proceeds. **First-install overlay notice:** on the very first install for this checkout, prints `installed mm-harness overlay v<version> → <path>` to stderr in human mode; in `--json` it appears solely as `mutations[]`. Use `--heal=off` to disable all auto-install and healing.
383
385
 
@@ -387,6 +389,14 @@ Installs the cached Runway iOS dev client onto a prepared mobile slot. It does n
387
389
  **Outputs:** `summary.json`, `trace.json`, `artifact-manifest.json`, screenshots/logs under `--artifacts-dir`.
388
390
  **Maps-to:** A:`run` (ROUTES-NOW); C/D:`run` (ABSORB-LATER — supply the auto adapter+artifacts defaults the typed CLI lacks).
389
391
 
392
+ ## `last` (resumability inspect verb)
393
+
394
+ **Synopsis:** `mm-harness last [--target <checkout>] [--runtime-dir <dir>] [--json]`
395
+
396
+ Reads `last-command.json`, the atomic per-checkout journal written before and after significant runtime/proof commands. The record contains redacted args, `running|pass|fail` verdict, exit code, explicit evidence/output paths, and start/finish timestamps. A process killed between writes remains `running`, allowing an agent to inspect rather than repeat side effects. Read-only discovery commands do not replace the record, and `last` never journals itself.
397
+
398
+ The file is mode `0600`; password, credential, auth, mnemonic, private-key, seed/SRP, secret, and token values are redacted before the first write. `--json` returns `{ command:"last", target, journalPath, status, exitCode, last }`. Missing or invalid state returns `LAST_NOT_FOUND` with an exact `userAction`.
399
+
390
400
  ## `doctor` (ROUTES-NOW → PROVE inspect verb)
391
401
 
392
402
  Readiness check for a checkout without launching the app. Doctor is the single place to understand the full slot context — **no hunting for files**. It also absorbs the retired `manifest` verb's validation function: the readiness checks include manifest well-formedness (schema valid, no unknown action references) and report the manifest path + protocol version in the explain-my-setup section. It includes three grounded sub-sections:
@@ -559,9 +569,9 @@ pretty-printed fallback. `manifest` is **RETIRED**: raw protocol dump rehomes to
559
569
 
560
570
  ### `actions` (ROUTES-NOW → DISCOVER verb)
561
571
 
562
- **Synopsis:** `mm-harness actions --adapter <p> [--json] [--action <name>] [--kind official|custom]`
572
+ **Synopsis:** `mm-harness actions [query] --adapter <p> [--json] [--categories | --category <name> | --action <name>]`
563
573
 
564
- **PRIMARY (agent):** `mm-harness actions --adapter mobile --json`
574
+ **PRIMARY (agent):** search from the task vocabulary, for example `mm-harness actions positions --adapter mobile --json`, then request one full `--action` schema. Use `--categories` only when the task does not provide a useful search term.
565
575
 
566
576
  **`--json` output shape** (grounded — `actions --adapter core --json` confirmed):
567
577
  ```json
@@ -571,6 +581,7 @@ pretty-printed fallback. `manifest` is **RETIRED**: raw protocol dump rehomes to
571
581
  {
572
582
  "name": "metamask.perps.read_positions",
573
583
  "kind": "custom",
584
+ "category": "perps",
574
585
  "description": "Read live Perps positions...",
575
586
  "fields": ["account", "action", "market", "markets", "mode", "selector", "side", "symbol", "symbols", "timeout_ms"],
576
587
  "examples": [{ "node": { "action": "metamask.perps.read_positions", "account": "...", "symbol": "BTC" } }]
@@ -578,7 +589,7 @@ pretty-printed fallback. `manifest` is **RETIRED**: raw protocol dump rehomes to
578
589
  ]
579
590
  }
580
591
  ```
581
- `kind` is `"official"` (engine built-ins) or `"custom"` (MetaMask adapter actions). `fields` lists every accepted parameter name. `examples[].node` is a copy-pasteable recipe node.
592
+ `kind` is `"official"` (engine built-ins) or `"custom"` (MetaMask adapter actions). `category` uses manifest metadata when present, then stable generic groups for protocol primitives and durable action namespaces (`ui.*` → `ui`, `metamask.perps.*` → `perps`). `fields` lists every accepted parameter name. `examples[].node` is a copy-pasteable recipe node.
582
593
 
583
594
  **Human form:** `mm-harness actions --adapter mobile` — one line per action: `<name> (<kind>) <description> fields=<f1,f2,...>`.
584
595
 
@@ -586,12 +597,13 @@ pretty-printed fallback. `manifest` is **RETIRED**: raw protocol dump rehomes to
586
597
  |---|---|---|---|---|---|
587
598
  | `--adapter <p>` | mobile\|extension\|core | auto-detect | `RECIPE_HARNESS_PLATFORM` | both | Target adapter · [DEFAULT-GAP] required today |
588
599
  | `--action <name>` | string | all | — | agent | Filter to one action; full schema + all examples |
589
- | `--kind <k>` | official\|custom | all | — | agent | Filter by action kind |
600
+ | `--categories` | bool | false | — | agent | Return only sorted category names and action counts |
601
+ | `--category <name>` | string | all | — | agent | Return actions from one namespace-derived category |
590
602
  | `--action-manifest <path>` | path | bundled | — | agent | Override manifest |
591
603
  | `--raw` | bool | false | — | agent | Dump the underlying action manifest JSON (protocol version, registry version, all entries in raw registry format — same output as `manifest --json` today; replaces the retired `manifest` verb) |
592
604
  | `--json` | bool | false | — | **agent PRIMARY** | Full schema + fields + examples per action |
593
605
 
594
- **[DISCOVERY-GAP]:** No `--action <name>` single-action filter today (all or nothing). No `--kind` filter. No keyword search across descriptions or fields.
606
+ The optional positional `query` searches names, categories, fields, and descriptions with typo tolerance. A single `--action` detail also returns up to five compact `relatedActions` names. Flow discovery remains separate.
595
607
  **Exit:** 0 / non-zero on engine error.
596
608
  **Maps-to:** A:`actions` (ROUTES-NOW); C/D:`actions` (ABSORB-LATER).
597
609
 
@@ -897,6 +909,36 @@ Every failure in `--json` mode produces an `error` field at the top level:
897
909
  | `retryable` | boolean | `true` = same command may succeed on retry; `false` = agent must change inputs or escalate |
898
910
  | `userAction` | string \| null | Exact next command or manual step |
899
911
 
912
+ Successful lifecycle transitions may add a top-level `next` command when exactly
913
+ one follow-up is correct (for example install → verify or stop → relaunch).
914
+ Discovery, read-only, and terminal evidence commands intentionally omit it rather
915
+ than inventing guidance. Agents must therefore treat `next` as optional.
916
+
917
+ Public command-grammar failures happen before runtime dispatch and always exit 2.
918
+ Their machine envelope is deliberately smaller and stable: agents branch on
919
+ `error.code`, never on prose.
920
+
921
+ ```json
922
+ {
923
+ "schemaVersion": 1,
924
+ "command": "actions",
925
+ "status": "fail",
926
+ "error": {
927
+ "code": "CLI_UNKNOWN_OPTION",
928
+ "message": "unknown option '--categoriez'. Valid options for mm-harness actions: ...",
929
+ "userAction": "Did you mean '--categories' instead of '--categoriez'? Try: mm-harness actions --adapter mobile"
930
+ },
931
+ "exitCode": 2
932
+ }
933
+ ```
934
+
935
+ Stable grammar codes are `CLI_UNKNOWN_COMMAND`, `CLI_UNKNOWN_OPTION`,
936
+ `CLI_MISSING_OPTION_VALUE`, `CLI_INVALID_OPTION_VALUE`,
937
+ `CLI_MISSING_POSITIONAL`, `CLI_INVALID_POSITIONAL`,
938
+ `CLI_EXCESS_POSITIONAL`, and `CLI_UNEXPECTED_PASSTHROUGH`. Every public
939
+ command is tracked in `docs/CLI-ERGONOMICS-AUDIT.md`; private adapter leaves keep
940
+ their own grammar behind explicit `--` passthrough.
941
+
900
942
  ## §5.2 Stable recovery codes (`recovered[]`)
901
943
 
902
944
  `recovered[]` in `--json` output lists what was healed. These are stable enum values — not prose, safe to match in agent code:
@@ -937,18 +979,20 @@ Every `--json` response includes `mutations[]` listing all side effects the comm
937
979
 
938
980
  ## §5.4 Phase state-machine
939
981
 
940
- Every `--json` response includes a `phase` field reflecting the last completed phase. `--json-stream` emits one event per transition. Agents can detect exactly where a failure occurred.
982
+ Summary `--json` responses retain their command-specific `phase` field. `--json-stream` emits transitions as they occur, so an agent can distinguish active work from a stalled command.
941
983
 
942
984
  **`run` phases:**
943
985
  ```
944
- resolve → installhealthcheckrecovervalidate → launch → execute
986
+ resolve → validateinstallhealthcheck → execute
945
987
  ```
946
988
 
947
989
  **`launch` phases:**
948
990
  ```
949
- resolve → install → healthcheck → recover → launch → verify
991
+ resolve → install → healthcheck → launch → verify
950
992
  ```
951
993
 
994
+ `recover` is conditional and appears immediately before a bounded recovery attempt; `verify` appears only for `launch --verify`.
995
+
952
996
  | Phase | What happens |
953
997
  |---|---|
954
998
  | `resolve` | Recipe/target/adapter resolved; library sources loaded |
@@ -962,20 +1006,20 @@ resolve → install → healthcheck → recover → launch → verify
962
1006
 
963
1007
  ## §5.5 JSONL event stream (`--json-stream`)
964
1008
 
965
- `--json-stream` emits one JSON object per line on stdout as the command progresses. Parseable with `jq -R 'fromjson?'`. Schema is versioned via `schemaVersion`.
1009
+ `run` and `launch` accept `--json-stream`. Each progress event is written as one complete JSON object plus a newline, so a piped reader can parse it before the command finishes. Schema is versioned via `schemaVersion`; every event also carries `command` and `ts`.
1010
+
1011
+ Stream mode owns stdout: existing human or subprocess output is routed to stderr. If both `--json` and `--json-stream` are supplied, stream mode wins. The existing summary `--json` bytes are unchanged when stream mode is absent.
966
1012
 
967
1013
  ```jsonl
968
- {"schemaVersion":1,"event":"phase","phase":"resolve","ts":"2026-07-02T10:00:00.000Z"}
969
- {"schemaVersion":1,"event":"phase","phase":"install","ts":"2026-07-02T10:00:00.100Z"}
970
- {"schemaVersion":1,"event":"mutation","mutation":{"type":"file","path":"/...","action":"created"},"ts":"..."}
971
- {"schemaVersion":1,"event":"phase","phase":"validate","ts":"..."}
972
- {"schemaVersion":1,"event":"phase","phase":"execute","ts":"..."}
973
- {"schemaVersion":1,"event":"node","index":0,"action":"metamask.wallet.unlock","status":"running","ts":"..."}
974
- {"schemaVersion":1,"event":"node","index":0,"action":"metamask.wallet.unlock","status":"passed","ts":"..."}
975
- {"schemaVersion":1,"event":"complete","status":"pass","exitCode":0,"recovered":[],"mutations":[...],"ts":"..."}
1014
+ {"schemaVersion":1,"command":"run","event":"phase","phase":"resolve","ts":"2026-07-02T10:00:00.000Z"}
1015
+ {"schemaVersion":1,"command":"run","event":"phase","phase":"validate","ts":"2026-07-02T10:00:00.100Z"}
1016
+ {"schemaVersion":1,"command":"run","event":"phase","phase":"execute","ts":"..."}
1017
+ {"schemaVersion":1,"command":"run","event":"node","nodeId":"unlock","action":"metamask.wallet.unlock","status":"running","ts":"..."}
1018
+ {"schemaVersion":1,"command":"run","event":"node","nodeId":"unlock","action":"metamask.wallet.unlock","status":"passed","ts":"..."}
1019
+ {"schemaVersion":1,"command":"run","event":"complete","status":"pass","exitCode":0,"reportPath":"/.../report.md","artifactManifestPath":"/.../artifact-manifest.json","recovered":[],"mutations":[],"ts":"..."}
976
1020
  ```
977
1021
 
978
- Event types: `phase` · `node` · `mutation` · `recovery` · `error` · `complete`. The terminal `complete` event always appears (even on failure) so agents have a clean sentinel.
1022
+ Event types: `phase` · `node` · `mutation` · `recovery` · `error` · `complete`. Node events use stable recipe `nodeId` values rather than an inferred numeric order. Exactly one terminal `complete` event is last on normal success or handled failure, giving agents a clean sentinel and compact evidence paths without repeating the full run result.
979
1023
 
980
1024
  ## §5.6 Exit code taxonomy
981
1025
 
@@ -189,14 +189,14 @@ The composition loop: `actions --json` → `call <action>` (try one via real eng
189
189
  | | |
190
190
  |---|---|
191
191
  | **Human form** | `mm-harness actions --adapter mobile` · `mm-harness call unlock` · `mm-harness flows` |
192
- | **Agent PRIMARY** | `mm-harness actions --adapter mobile --json` `{ adapter, actions: [{ name, kind, description, fields, examples }] }` |
192
+ | **Agent PRIMARY** | `mm-harness actions <task-term> --adapter mobile --json`, then one focused `--action <name>` schema; use `--categories` only without a useful term |
193
193
  | | `mm-harness call metamask.perps.read_positions --arg symbol=BTC --adapter core --json` → `{ action, resolvedAction, args, trace, evidence, recovered, mutations, exitCode, schemaVersion }` (same shape as `run --json`, one-node subset) |
194
194
  | | `mm-harness flows --json` → `{ sources, flows: [{ ref, source, file, description?, requiredParams?, shadows?, lastVerified? }] }` |
195
195
  | **Plan before run** | `mm-harness run recipe.json --plan --json` → `{ plan[], validation: { status, findings }, schemaVersion }` (adapter-aware; exit 5 on errors) |
196
196
 
197
197
  | Today (real) | → mm-harness | Status |
198
198
  |---|---|---|
199
- | `metamask-recipe actions --adapter …` | `mm-harness actions --adapter …` | ROUTES-NOW · [DEFAULT-GAP] `--adapter` required · [DISCOVERY-GAP] no `--action <name>` filter, no `--kind` filter, no keyword search |
199
+ | `metamask-recipe actions --adapter …` | `mm-harness actions --adapter …` | ROUTES-NOW · auto-detects in a checkout · bounded category/action views shipped · [DISCOVERY-GAP] no keyword search |
200
200
  | `metamask-recipe flows list\|promote` | `mm-harness flows [\|promote]` | ROUTES-NOW · `flows` = browse the reusable flow library (compose, don't rewrite); `promote` publishes a proven flow up a tier · bare `flows` = list · [DISCOVERY-GAP] no keyword filter, no per-flow body fetch, no platform filter |
201
201
  | `mm-recipe`/`mme-recipe` `actions`/`doctor` (auto adapter) | same `mm-harness` verbs | ABSORB-LATER — porcelain supplies missing adapter default |
202
202
  | hook layer `run-action app.unlock` | `mm-harness call unlock` (fuzzy: `unlock` → `metamask.wallet.unlock`) — **one-node recipe via real engine path** | REAL (wave 2) · ambiguous → exit 2 listing candidates |
@@ -27,6 +27,8 @@ The CLI answers relative to the checkout it is standing in.
27
27
  Long operations show intent immediately and progress continuously.
28
28
  - First line within milliseconds: what is being done, where (device/port), and
29
29
  where the full log lives.
30
+ - Add an explicit first line only when work can block before existing output. Fast
31
+ commands use their result as acknowledgement; they do not print a redundant banner.
30
32
  - Leaf output streams live to stderr — `--json` reserves stdout for the envelope,
31
33
  it never buys silence. Quiet stretches get heartbeats with elapsed time.
32
34
  - Each dev server a command starts gets its tmux log-tail window at launch time
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.15.2",
3
+ "version": "0.17.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
7
7
  },
8
8
  "scripts": {
9
9
  "build": "node scripts/build-dist.mjs",
10
+ "audit:cli-ergonomics": "node scripts/generate-cli-ergonomics-audit.mjs",
10
11
  "prepack": "npm run build",
11
12
  "dev:link-farmslot": "node scripts/link-local-farmslot.mjs",
12
13
  "check": "node scripts/check.mjs",