@deeeed/metamask-harness 0.92.17 → 0.92.18

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,19 +2,33 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.92.18 - 2026-10-16
6
+
5
7
  ### Changed
6
8
 
7
- - Point task initialization, closeout and PR-body documentation at the shared Farmslot commands.
8
- - Refresh Terminal contract goldens for the shipped console-error window, capture-provider metadata and engine evidence inventories.
9
+ - Consume the published parent acceptance inheritance worker packages so hosted task tooling uses the matching Protocol 0.53.0 contracts.
10
+
11
+ ### Removed
12
+
13
+ - Remove the retired `pr-body` route and duplicate renderer after callers moved to `farmslot recipe artifacts --format markdown`.
14
+
15
+ ### Fixed
16
+
17
+ - Recognize shared host environment lookups in the feature inventory and record the retired private Extension recorder PID handshake.
9
18
 
10
19
  ## 0.92.17 - 2026-10-16
11
20
 
12
21
  ### Changed
13
22
 
14
23
  - Consume the published pipeline-review contracts, native review results and aligned worker dependency set.
24
+ - Point task initialization, closeout and PR-body documentation at the shared Farmslot commands.
15
25
 
16
26
  ## 0.92.16 - 2026-10-16
17
27
 
28
+ ### Changed
29
+
30
+ - Refresh Terminal contract goldens for the shipped console-error window, capture-provider metadata and engine evidence inventories.
31
+
18
32
  ### Fixed
19
33
 
20
34
  - Consume the published hosted-task runtime and aligned dependency set so task tooling retains owning workspaces, declared dependencies and hoisted peers.
@@ -11,7 +11,6 @@ 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"] },
15
14
  { name: "config", desc: "Per-engineer locations and optional advisor settings", args: ["list", "path", "get", "set", "unset"], flags: ["--json"] },
16
15
  { name: "tutorial", desc: "Open the visual recipe tutorial", flags: ["--json", "--no-open"] },
17
16
  { name: "setup-base", desc: "Bootstrap numbered product checkouts", flags: ["--dir", "--counts", "--only", "--dry-run", "--force", "--json", "--show-config", "--reset-config", "--skip-harness-update"] },
@@ -175,36 +175,6 @@ 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
- --command <text> Recipe invocation to show above the run log
194
- --out <file> Write elsewhere than artifacts/pr-body.md
195
- --json Machine-readable result
196
-
197
- Example:
198
- mm-harness pr-body render temp/tasks/dev/TAT-1 --json`,
199
- contract: {
200
- options: options(HELP, JSON, {
201
- "--command": value(),
202
- "--out": value()
203
- }),
204
- positionals: [{ label: "action", choices: ["render"] }, { label: "task-dir" }],
205
- minimumPositionals: 2
206
- }
207
- },
208
178
  {
209
179
  name: "config",
210
180
  summary: "Per-engineer locations and optional recipe advisor settings.",
@@ -48,7 +48,6 @@ 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";
52
51
  import { handlePrepare } from "./commands/prepare.js";
53
52
  import { handleProvision } from "./commands/provision.js";
54
53
  import { handleRecipeQuality } from "./commands/recipe-quality.js";
@@ -140,7 +139,6 @@ const PUBLIC_RUN = {
140
139
  help: { run: (args) => handleHelp(args, { packageRoot, harnessVersion: hostVersion() }), nudge: false },
141
140
  review: { run: handleReview },
142
141
  domain: { run: handleDomain },
143
- "pr-body": { run: handlePrBody },
144
142
  config: { run: handleConfig },
145
143
  tutorial: { run: (args) => handleTutorial(args, { harnessVersion: hostVersion() }), exit: "now", nudge: false },
146
144
  "setup-base": { run: handleSetupBase, raw: true, exit: "now", nudge: false },
@@ -200,7 +198,7 @@ const HELP_GROUPS = [
200
198
  {
201
199
  title: "PROVE",
202
200
  blurb: "run recipes and inspect readiness",
203
- commands: ["run", "last", "doctor", "check", "checklist", "review", "pr-body", "recipe-quality"]
201
+ commands: ["run", "last", "doctor", "check", "checklist", "review", "recipe-quality"]
204
202
  },
205
203
  {
206
204
  title: "RUNTIME OVERLAY",
@@ -62,7 +62,7 @@ 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
- | `pr-body render <taskDir> --json` (cwd = task dir) | gateway `pr-body-render.ts` (`pr_body_cmd`) | on failure `JSON.parse(stdout).error`, else stderr; then `artifacts/pr-body.md` | `task/pr-body-render-*` |
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 |
66
66
  | `review checklist --out <file> [--domain <d>] [--since <sha>]` | domain fixtures, review templates | the written file | `task/review-checklist` |
67
67
  | `check diff …`, `recipe-quality build …` | templates | exit code | not covered: run repo lint/test tooling |
68
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.92.17",
3
+ "version": "0.92.18",
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.32",
25
- "@farmslot/adapter-rn": "0.22.21",
26
- "@farmslot/adapter-sdk": "0.17.0",
27
- "@farmslot/adapter-web": "0.10.12",
28
- "@farmslot/agent-runtime": "0.27.0",
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",
29
29
  "@farmslot/handoff": "^0.3.1",
30
- "@farmslot/protocol": "0.52.0",
31
- "@farmslot/recipe-cli": "0.22.4",
32
- "@farmslot/recipe-runner": "0.38.3",
30
+ "@farmslot/protocol": "0.53.0",
31
+ "@farmslot/recipe-cli": "0.22.5",
32
+ "@farmslot/recipe-runner": "0.38.4",
33
33
  "agent-device": "0.19.3",
34
34
  "commander": "^12.0.0",
35
35
  "esbuild": "0.28.1",
@@ -1,31 +0,0 @@
1
- import { optionFlag, optionString, parseArgs, usageError } from "@farmslot/recipe-cli/harness";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { BODY_FILE, renderPrBody } from "../pr-body/render.js";
5
- const USAGE = "Usage: mm-harness pr-body render <task-dir> [--command <text>] [--out <file>] [--json]";
6
- async function handlePrBody(argv) {
7
- const parsed = parseArgs(argv);
8
- const [action, taskArg] = parsed.positional;
9
- if (action !== "render" || !taskArg) throw usageError(USAGE);
10
- const taskDir = path.resolve(taskArg);
11
- if (!fs.existsSync(path.join(taskDir, "artifacts"))) {
12
- throw usageError(`${taskDir} has no artifacts/ directory; pass the task directory mm-harness task init wrote.`);
13
- }
14
- const command = optionString(parsed.options, "command");
15
- const result = renderPrBody({ taskDir, ...command ? { command } : {} });
16
- const out = path.resolve(optionString(parsed.options, "out") ?? path.join(taskDir, "artifacts", BODY_FILE));
17
- fs.mkdirSync(path.dirname(out), { recursive: true });
18
- fs.writeFileSync(out, result.body);
19
- if (optionFlag(parsed.options, "json")) {
20
- process.stdout.write(`${JSON.stringify({ command: "pr-body", subcommand: "render", status: "ok", taskDir, out, ...result, body: void 0 }, null, 2)}
21
- `);
22
- } else {
23
- process.stdout.write(`pr body: ${out}
24
- ${result.sections.join("\n ")}
25
- `);
26
- }
27
- return 0;
28
- }
29
- export {
30
- handlePrBody
31
- };
@@ -1,144 +0,0 @@
1
- import fs from "node:fs";
2
- import { createRequire } from "node:module";
3
- import path from "node:path";
4
- const { readRecipeAttemptSelection } = createRequire(import.meta.url)(
5
- "@farmslot/agent-runtime/scripts/retained-recipe-attempt.cjs"
6
- );
7
- const PROSE_FILE = "pr-description.md";
8
- const BODY_FILE = "pr-body.md";
9
- const RECIPE_HEADING = "## **Validation Recipe**";
10
- const LOGS_HEADING = "## **Validation Logs**";
11
- const WORKFLOW_HEADING = "## **Recipe Workflow**";
12
- function headingKey(heading) {
13
- return heading.replace(/^#+\s*/, "").replace(/[*_`]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
14
- }
15
- const MACHINE_KEYS = new Set([RECIPE_HEADING, LOGS_HEADING, WORKFLOW_HEADING].map(headingKey));
16
- function levelTwoHeadingLines(lines) {
17
- const found = [];
18
- lines.forEach((line, index) => {
19
- if (/^ {0,3}##(?!#)\s+\S/.test(line)) found.push(index);
20
- });
21
- return found;
22
- }
23
- function fenceFor(content) {
24
- let longest = 0;
25
- for (const run of content.match(/`+/g) ?? []) longest = Math.max(longest, run.length);
26
- return "`".repeat(Math.max(3, longest + 1));
27
- }
28
- function fenced(content, info = "") {
29
- const fence = fenceFor(content);
30
- return `${fence}${info}
31
- ${content.replace(/\n+$/, "")}
32
- ${fence}`;
33
- }
34
- function escapeHtml(text) {
35
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
36
- }
37
- function countRecipeNodes(recipe) {
38
- if (!recipe || typeof recipe !== "object") return 0;
39
- const record = recipe;
40
- if (Array.isArray(record.nodes)) return record.nodes.length;
41
- if (Array.isArray(record.steps)) return record.steps.length;
42
- const workflow = record.workflow;
43
- if (!workflow || typeof workflow !== "object") return 0;
44
- const graph = workflow.nodes;
45
- if (graph && typeof graph === "object" && !Array.isArray(graph)) return Object.keys(graph).length;
46
- return Object.values(workflow).reduce(
47
- (total, value) => total + (Array.isArray(value) ? value.length : 0),
48
- 0
49
- );
50
- }
51
- function recipeSection(file) {
52
- const recipe = JSON.parse(fs.readFileSync(file, "utf8"));
53
- const nodes = countRecipeNodes(recipe);
54
- const record = recipe && typeof recipe === "object" ? recipe : {};
55
- const title = typeof record.title === "string" ? record.title : typeof record.description === "string" ? record.description : "";
56
- const summary = escapeHtml(`recipe.json (${nodes} nodes${title ? ` \u2014 ${title.slice(0, 120)}` : ""})`);
57
- return {
58
- nodes,
59
- body: [`<details><summary>${summary}</summary>`, "", fenced(JSON.stringify(recipe, null, 2), "json"), "</details>"].join("\n")
60
- };
61
- }
62
- function logsSection(file, command) {
63
- const report = fs.readFileSync(file, "utf8");
64
- const status = /^Status:\s*(\S+)/m.exec(report)?.[1];
65
- const nodes = /^Nodes:\s*(.+?)\s*$/m.exec(report)?.[1];
66
- const summary = escapeHtml(status || nodes ? `Full output (${[nodes, status].filter(Boolean).join(", ")})` : "Full output");
67
- const lines = [];
68
- if (command) lines.push("Command:", "", fenced(command, "bash"), "");
69
- lines.push(`<details><summary>${summary}</summary>`, "", fenced(report), "</details>");
70
- return lines.join("\n");
71
- }
72
- function trimEnd(lines) {
73
- let end = lines.length;
74
- while (end > 0 && !lines[end - 1].trim()) end -= 1;
75
- return lines.slice(0, end);
76
- }
77
- function renderPrBody(input) {
78
- const artifacts = path.join(input.taskDir, "artifacts");
79
- const prosePath = path.join(artifacts, PROSE_FILE);
80
- if (!fs.existsSync(prosePath)) {
81
- throw new Error(`Missing ${path.relative(input.taskDir, prosePath)}: write the PR description there first.`);
82
- }
83
- const recipePath = path.join(artifacts, "recipe.json");
84
- const selected = readRecipeAttemptSelection(input.taskDir);
85
- const reportRoot = selected?.relativeArtifactRoot ?? "recipe-run";
86
- const reportPath = path.join(artifacts, reportRoot, "report.md");
87
- const workflowPath = path.join(artifacts, "workflow.mmd");
88
- const machine = [];
89
- let recipe = null;
90
- if (fs.existsSync(recipePath)) {
91
- const rendered = recipeSection(recipePath);
92
- recipe = { nodes: rendered.nodes, path: path.relative(input.taskDir, recipePath) };
93
- machine.push([RECIPE_HEADING, rendered.body]);
94
- } else {
95
- machine.push([RECIPE_HEADING, "Not applicable: this task has no `artifacts/recipe.json`."]);
96
- }
97
- const hasReport = fs.existsSync(reportPath);
98
- machine.push([
99
- LOGS_HEADING,
100
- hasReport ? logsSection(reportPath, input.command) : `Not applicable: no recipe run report under \`artifacts/${reportRoot}/\`.`
101
- ]);
102
- const hasWorkflow = fs.existsSync(workflowPath);
103
- if (hasWorkflow) {
104
- machine.push([
105
- WORKFLOW_HEADING,
106
- ["<details><summary>workflow.mmd</summary>", "", fenced(fs.readFileSync(workflowPath, "utf8"), "mermaid"), "</details>"].join("\n")
107
- ]);
108
- }
109
- const lines = fs.readFileSync(prosePath, "utf8").replace(/\r\n?/g, "\n").split("\n");
110
- const headings = levelTwoHeadingLines(lines);
111
- const keep = new Array(lines.length).fill(true);
112
- let insertAt = lines.length;
113
- headings.forEach((start, at) => {
114
- const end = headings[at + 1] ?? lines.length;
115
- const key = headingKey(lines[start]);
116
- if (MACHINE_KEYS.has(key)) keep.fill(false, start, end);
117
- else if (insertAt === lines.length && /checklist/.test(key)) insertAt = start;
118
- });
119
- const before = lines.slice(0, insertAt).filter((_, index) => keep[index]);
120
- const after = lines.slice(insertAt).filter((_, index) => keep[insertAt + index]);
121
- const generated = machine.flatMap(([heading, body2]) => [heading, "", body2, ""]);
122
- const body = [...trimEnd(before), "", ...generated, ...after].join("\n").replace(/\n{3,}/g, "\n\n").replace(/\n*$/, "\n");
123
- return {
124
- body,
125
- sections: machine.map(([heading]) => heading),
126
- recipe,
127
- run: hasReport ? { path: path.relative(input.taskDir, reportPath) } : null,
128
- workflow: hasWorkflow ? { path: path.relative(input.taskDir, workflowPath) } : null
129
- };
130
- }
131
- export {
132
- BODY_FILE,
133
- LOGS_HEADING,
134
- PROSE_FILE,
135
- RECIPE_HEADING,
136
- WORKFLOW_HEADING,
137
- countRecipeNodes,
138
- escapeHtml,
139
- fenceFor,
140
- fenced,
141
- headingKey,
142
- levelTwoHeadingLines,
143
- renderPrBody
144
- };