@deeeed/metamask-harness 0.92.20 → 0.92.21

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.92.21 - 2026-10-17
6
+
7
+ ### Added
8
+
9
+ - Restore `mm-harness pr-body render` as a thin command over the shared Farmslot renderer.
10
+
11
+ ### Changed
12
+
13
+ - Pin the `@farmslot` packages to the release set with `@farmslot/recipe-cli` 0.23.0 (protocol 0.54.0, recipe-runner 0.39.0, adapter-sdk 0.18.0, agent-runtime 0.29.0, adapter-web 0.10.14, adapter-node 0.1.34, adapter-rn 0.22.23).
14
+
15
+ ### Fixed
16
+
17
+ - `pr-body render` takes the Validation Recipe from the selected attempt's captured `recipe.json` (or the task's single retained package), not the editable `artifacts/recipe.json`, and names the run, runner, runner revision and recipe digest above its log.
18
+ - `pr-body render` refuses a selected attempt that fails artifact validation or lacks its recipe or report, and refuses two candidate packages when no attempt is selected; it writes nothing in either case.
19
+ - `pr-body render --out` refuses to overwrite the authored description, recipe, workflow, attempt selection or retained evidence.
20
+ - `pr-body render` refuses symlinked or oversized (over 4 MiB) inputs, converts CRLF in fenced logs to LF, and creates new output files owner-only without following a symlink.
21
+
5
22
  ## 0.92.20 - 2026-10-17
6
23
 
7
24
  ### Removed
package/README.md CHANGED
@@ -400,16 +400,16 @@ check for upstream updates. Keep the selected library unchanged during a run.
400
400
  ```bash
401
401
  # Write artifacts/pr-description.md in the repository PR template shape (prose
402
402
  # only), then render the publishable body.
403
- farmslot recipe artifacts temp/tasks/dev/TAT-1 --format markdown \
404
- --out temp/tasks/dev/TAT-1/artifacts/pr-body.md \
405
- --command 'farmslot recipe run artifacts/recipe.json'
403
+ mm-harness pr-body render temp/tasks/dev/TAT-1 --command 'mm-harness run artifacts/recipe.json'
406
404
  ```
407
405
 
408
- The Markdown renderer writes `artifacts/pr-body.md`: the description with the "Validation
406
+ `pr-body render` writes `artifacts/pr-body.md`: the description with the "Validation
409
407
  Recipe" and "Validation Logs" sections rendered from `artifacts/recipe.json` and
410
408
  `artifacts/recipe-run/report.md` (and "Recipe Workflow" from `artifacts/workflow.mmd`
411
409
  when it exists), replacing any pasted by hand. The worker never pastes artifacts
412
- into markdown, so fences cannot break.
410
+ into markdown, so fences cannot break. When a passing run recorded the task's
411
+ selected attempt, both sections come from that attempt's validated package; a package
412
+ that fails validation is refused, and nothing is written.
413
413
 
414
414
  ## Review a change
415
415
 
@@ -11,6 +11,7 @@ const SPEC = {
11
11
  { name: "help", desc: "Load version-matched recipe guidance (help review composes the review guide)", args: ["review"], flags: ["--json", "--adapter", "--target", "--domain"] },
12
12
  { name: "review", desc: "Materialize a review checklist composed from the base review and a team library", args: ["checklist"], flags: ["--domain", "--since", "--base", "--out", "--adapter", "--target", "--json"] },
13
13
  { name: "domain", desc: "Which team library owns the change (declared value, else owned-paths.json)", flags: ["--domain", "--base", "--adapter", "--target", "--json"] },
14
+ { name: "pr-body", desc: "Render the publishable PR body: pr-description.md plus the recipe and run sections built from artifacts", args: ["render", "<task-dir>"], flags: ["--command", "--out", "--json"] },
14
15
  { name: "config", desc: "Per-engineer locations and optional advisor settings", args: ["list", "path", "get", "set", "unset"], flags: ["--json"] },
15
16
  { name: "tutorial", desc: "Open the visual recipe tutorial", flags: ["--json", "--no-open"] },
16
17
  { name: "setup-base", desc: "Bootstrap numbered product checkouts", flags: ["--dir", "--counts", "--only", "--dry-run", "--force", "--json", "--show-config", "--reset-config", "--skip-harness-update"] },
@@ -175,6 +175,40 @@ Example:
175
175
  })
176
176
  }
177
177
  },
178
+ {
179
+ name: "pr-body",
180
+ summary: "Render the publishable PR body: the authored description plus the recipe and run log sections built from artifacts.",
181
+ example: "mm-harness pr-body render temp/tasks/dev/TAT-1",
182
+ helpText: `mm-harness pr-body render <task-dir> [flags]
183
+
184
+ Reads <task-dir>/artifacts/pr-description.md (the PR body the worker wrote in
185
+ the repository PR template shape) and inserts the machine sections rendered
186
+ from artifacts: "Validation Recipe" from artifacts/recipe.json, "Validation
187
+ Logs" from artifacts/recipe-run/report.md, and "Recipe Workflow" from
188
+ artifacts/workflow.mmd when it exists. Any such section the worker pasted by
189
+ hand is replaced. The sections go before the first checklist section, else at
190
+ the end. Fences are always longer than any backtick run in the content and
191
+ close on their own line. Writes <task-dir>/artifacts/pr-body.md.
192
+
193
+ When a passing run recorded the task's selected attempt, both sections come
194
+ from that attempt's package, which must pass validation or nothing is written.
195
+
196
+ --command <text> Recipe invocation to show above the run log
197
+ --out <file> Write elsewhere than artifacts/pr-body.md (never over an
198
+ authored input or retained evidence)
199
+ --json Machine-readable result
200
+
201
+ Example:
202
+ mm-harness pr-body render temp/tasks/dev/TAT-1 --json`,
203
+ contract: {
204
+ options: options(HELP, JSON, {
205
+ "--command": value(),
206
+ "--out": value()
207
+ }),
208
+ positionals: [{ label: "action", choices: ["render"] }, { label: "task-dir" }],
209
+ minimumPositionals: 2
210
+ }
211
+ },
178
212
  {
179
213
  name: "config",
180
214
  summary: "Per-engineer locations and optional recipe advisor settings.",
@@ -0,0 +1,29 @@
1
+ import { optionFlag, optionString, parseArgs, renderRetainedArtifactsMarkdown, usageError } from "@farmslot/recipe-cli/harness";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ const USAGE = "Usage: mm-harness pr-body render <task-dir> [--command <text>] [--out <file>] [--json]";
5
+ async function handlePrBody(argv) {
6
+ const parsed = parseArgs(argv);
7
+ const [action, taskArg] = parsed.positional;
8
+ if (action !== "render" || !taskArg) throw usageError(USAGE);
9
+ const taskDir = path.resolve(taskArg);
10
+ if (!fs.existsSync(path.join(taskDir, "artifacts"))) {
11
+ throw usageError(`${taskDir} has no artifacts/ directory; pass the task directory mm-harness task init wrote.`);
12
+ }
13
+ const command = optionString(parsed.options, "command");
14
+ const out = path.resolve(optionString(parsed.options, "out") ?? path.join(taskDir, "artifacts", "pr-body.md"));
15
+ const { result } = await renderRetainedArtifactsMarkdown({ directory: taskDir, command, out, format: "markdown" });
16
+ const { sections = [], recipe, run, workflow } = result;
17
+ if (optionFlag(parsed.options, "json")) {
18
+ process.stdout.write(`${JSON.stringify({ command: "pr-body", subcommand: "render", status: "ok", taskDir, out, sections, recipe, run, workflow }, null, 2)}
19
+ `);
20
+ } else {
21
+ process.stdout.write(`pr body: ${out}
22
+ ${sections.join("\n ")}
23
+ `);
24
+ }
25
+ return 0;
26
+ }
27
+ export {
28
+ handlePrBody
29
+ };
@@ -48,6 +48,7 @@ import { handleHelp } from "./commands/help.js";
48
48
  import { handleLogs } from "./commands/logs.js";
49
49
  import { overlayCommandOptions } from "./commands/overlay.js";
50
50
  import { handlePin } from "./commands/pin.js";
51
+ import { handlePrBody } from "./commands/pr-body.js";
51
52
  import { handlePrepare } from "./commands/prepare.js";
52
53
  import { handleProvision } from "./commands/provision.js";
53
54
  import { handleRecipeQuality } from "./commands/recipe-quality.js";
@@ -138,6 +139,7 @@ const PUBLIC_RUN = {
138
139
  help: { run: (args) => handleHelp(args, { packageRoot, harnessVersion: hostVersion() }), nudge: false },
139
140
  review: { run: handleReview },
140
141
  domain: { run: handleDomain },
142
+ "pr-body": { run: handlePrBody },
141
143
  config: { run: handleConfig },
142
144
  tutorial: { run: (args) => handleTutorial(args, { harnessVersion: hostVersion() }), exit: "now", nudge: false },
143
145
  "setup-base": { run: handleSetupBase, raw: true, exit: "now", nudge: false },
@@ -196,7 +198,7 @@ const HELP_GROUPS = [
196
198
  {
197
199
  title: "PROVE",
198
200
  blurb: "run recipes and inspect readiness",
199
- commands: ["run", "last", "doctor", "check", "checklist", "review", "recipe-quality"]
201
+ commands: ["run", "last", "doctor", "check", "checklist", "review", "pr-body", "recipe-quality"]
200
202
  },
201
203
  {
202
204
  title: "RUNTIME OVERLAY",
@@ -62,7 +62,8 @@ Callers scanned (2026-10-02):
62
62
  | Command | Caller | Consumes | G: |
63
63
  |---|---|---|---|
64
64
  | `checklist mark <taskDir> {start\|<n>\|complete [--mark-last]\|blocked --reason <s>\|no-change --reason <s>\|--help}` | `{{TASK_DIR}}/mark` shim (`mark_cmd`) | exit code; `SIGNAL.json`, `CHECKLIST.md` | `task/mark-*` |
65
- | `farmslot recipe artifacts <taskDir> --format markdown --out <file> --json` | migrated pack hooks and PR-description skills | successful exit, structured `error.message`, explicit `out`; no implicit output file | shared recipe-cli artifact contracts; retained Markdown baselines |
65
+ | `pr-body render <taskDir> [--out <file>] --json` (cwd = task dir) | MetaMask engineers and skills; the gateway `pr-body-render.ts` when a pack's `pr_body_cmd` names it | on failure `JSON.parse(stdout).error`, else stderr; then `--out`, else `artifacts/pr-body.md` | `task/pr-body-render-*` |
66
+ | `farmslot recipe artifacts <taskDir> --format markdown --out <file> --json` | farm pack hooks (`vars.pr_body_cmd`) and PR-description skills on the farm | successful exit, structured `error.message`, explicit `out`; no implicit output file | shared recipe-cli artifact contracts; retained Markdown baselines |
66
67
  | `review checklist --out <file> [--domain <d>] [--since <sha>]` | domain fixtures, review templates | the written file | `task/review-checklist` |
67
68
  | `check diff …`, `recipe-quality build …` | templates | exit code | not covered: run repo lint/test tooling |
68
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.92.20",
3
+ "version": "0.92.21",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -21,15 +21,15 @@
21
21
  "check:syntax": "find . -name '*.mjs' -print0 | xargs -0 -n1 node --check"
22
22
  },
23
23
  "dependencies": {
24
- "@farmslot/adapter-node": "0.1.33",
25
- "@farmslot/adapter-rn": "0.22.22",
26
- "@farmslot/adapter-sdk": "0.17.1",
27
- "@farmslot/adapter-web": "0.10.13",
28
- "@farmslot/agent-runtime": "0.28.0",
24
+ "@farmslot/adapter-node": "0.1.34",
25
+ "@farmslot/adapter-rn": "0.22.23",
26
+ "@farmslot/adapter-sdk": "0.18.0",
27
+ "@farmslot/adapter-web": "0.10.14",
28
+ "@farmslot/agent-runtime": "0.29.0",
29
29
  "@farmslot/handoff": "^0.3.1",
30
- "@farmslot/protocol": "0.53.0",
31
- "@farmslot/recipe-cli": "0.22.5",
32
- "@farmslot/recipe-runner": "0.38.4",
30
+ "@farmslot/protocol": "0.54.0",
31
+ "@farmslot/recipe-cli": "0.23.0",
32
+ "@farmslot/recipe-runner": "0.39.0",
33
33
  "agent-device": "0.19.3",
34
34
  "commander": "^12.0.0",
35
35
  "esbuild": "0.28.1",