@deeeed/metamask-harness 0.92.21 → 0.92.23

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.
@@ -9,22 +9,36 @@ import {
9
9
  createHarnessCli,
10
10
  handleActions,
11
11
  handleCall,
12
+ handleChecklist,
13
+ handleDoctor,
14
+ handleExecutionTemplate,
12
15
  handleHarness,
13
16
  handleLast,
14
17
  handleLaunch,
18
+ handleLogs,
19
+ handlePrepare,
20
+ handleRecipeQuality,
15
21
  handleReload,
22
+ handleReview,
16
23
  handleRun,
24
+ handleStatus,
17
25
  handleStop,
26
+ handleTaskInit,
18
27
  harnessHost,
19
28
  optionValues,
20
29
  parseArgs,
30
+ resolveAdapter,
21
31
  targetPath
22
32
  } from "@farmslot/recipe-cli/harness";
23
33
  import { handleStopViewers } from "./commands/stop.js";
24
- import { stopOptions } from "./provider-lifecycle.js";
34
+ import { cliDoctorOptions, configureProviderReadiness } from "./provider-doctor.js";
35
+ import { cliRecipeQualityOptions, cliReviewOptions, helperCommandOptions } from "./provider-helpers.js";
36
+ import { cliStatusOptions, prepareOptions, stopOptions } from "./provider-lifecycle.js";
37
+ import { createLogsOptions } from "./provider-logs.js";
25
38
  import { adoptPluginAdapter, afterAdapterLoad, configuredLibrarySources } from "./adapters/plugin-surface.js";
26
39
  import { withMainnetOrderAbort } from "./adapters/perps-mainnet-cleanup.js";
27
40
  import { adapterRegistry } from "./adapters/registry.js";
41
+ import { getAdapterSurface } from "./adapters/surface.js";
28
42
  import {
29
43
  explainMissingAdapterLibrary,
30
44
  HIDDEN_COMMANDS,
@@ -34,32 +48,22 @@ import {
34
48
  withAdapterList
35
49
  } from "./command-contract.js";
36
50
  import { handleCheck } from "./commands/check.js";
37
- import { handleChecklist } from "./commands/checklist.js";
38
51
  import { handleCompletionCandidates, invalidateCompletionCache } from "./commands/completion-candidates.js";
39
52
  import { handleConfig } from "./commands/config.js";
40
53
  import { handleDebug } from "./commands/debug.js";
41
54
  import { applyDeviceTargeting } from "./commands/device-target.js";
42
- import { handleDoctor } from "./commands/doctor.js";
43
- import { handleDomain } from "./commands/domain.js";
44
55
  import { handleEnsureReady } from "./commands/ensure-ready.js";
45
- import { handleExecutionTemplate } from "./commands/execution-template.js";
46
56
  import { handleFixtures } from "./commands/fixtures.js";
47
57
  import { handleHelp } from "./commands/help.js";
48
- import { handleLogs } from "./commands/logs.js";
49
58
  import { overlayCommandOptions } from "./commands/overlay.js";
50
59
  import { handlePin } from "./commands/pin.js";
51
60
  import { handlePrBody } from "./commands/pr-body.js";
52
- import { handlePrepare } from "./commands/prepare.js";
53
61
  import { handleProvision } from "./commands/provision.js";
54
- import { handleRecipeQuality } from "./commands/recipe-quality.js";
55
62
  import { handleResolveExtension } from "./commands/resolve-extension.js";
56
- import { handleReview } from "./commands/review.js";
57
63
  import { handleRuntimeDecision } from "./commands/runtime-decision.js";
58
64
  import { handleRuntimeLaunch } from "./commands/runtime-launch.js";
59
65
  import { handleSelfTest } from "./commands/self-test.js";
60
66
  import { handleSetupBase } from "./commands/setup-base.js";
61
- import { handleStatus } from "./commands/status.js";
62
- import { handleTaskInit } from "./commands/task-init.js";
63
67
  import { handleTutorial } from "./commands/tutorial.js";
64
68
  import { handleUpdate, maybeNudge } from "./commands/update.js";
65
69
  import { metaMaskEngine } from "./engine.js";
@@ -114,6 +118,20 @@ function overlay(command) {
114
118
  return code;
115
119
  };
116
120
  }
121
+ const LOGS_OPTIONS = PUBLIC_COMMANDS.find((command) => command.name === "logs").contract.options;
122
+ function logsOptions(options) {
123
+ const surface = () => createLogsOptions(getAdapterSurface(resolveAdapter(options).adapter));
124
+ return {
125
+ compact: (request) => surface().compact(request),
126
+ window: (request) => surface().window(request),
127
+ announce: ({ source, logFile }) => process.stderr.write(
128
+ `${source} log: ${logFile}
129
+ tail it yourself: tail -f ${logFile} \xB7 page it: less +F ${logFile}
130
+ following below (Ctrl-C to stop) \u2026
131
+ `
132
+ )
133
+ };
134
+ }
117
135
  function invocationLibraries() {
118
136
  return optionValues(process.argv.slice(2), "--library");
119
137
  }
@@ -137,16 +155,16 @@ function withManifestEnvelope(command, run) {
137
155
  }
138
156
  const PUBLIC_RUN = {
139
157
  help: { run: (args) => handleHelp(args, { packageRoot, harnessVersion: hostVersion() }), nudge: false },
140
- review: { run: handleReview },
141
- domain: { run: handleDomain },
158
+ review: { run: (args) => handleReview(args, cliReviewOptions) },
159
+ domain: { run: (args) => handleReview(["domain", ...args], cliReviewOptions) },
142
160
  "pr-body": { run: handlePrBody },
143
161
  config: { run: handleConfig },
144
162
  tutorial: { run: (args) => handleTutorial(args, { harnessVersion: hostVersion() }), exit: "now", nudge: false },
145
163
  "setup-base": { run: handleSetupBase, raw: true, exit: "now", nudge: false },
146
- status: { run: (args) => handleStatus(parseArgs(args)) },
147
- checklist: { run: handleChecklist },
148
- prepare: { run: handlePrepare },
149
- task: { run: handleTaskInit },
164
+ status: { run: (args) => handleStatus(parseArgs(args), cliStatusOptions) },
165
+ checklist: { run: (args) => handleChecklist(args, helperCommandOptions.task) },
166
+ prepare: { run: (args) => handlePrepare(args, prepareOptions) },
167
+ task: { run: (args) => handleTaskInit(args, { surface: "skill" }) },
150
168
  "execution-template": { run: handleExecutionTemplate },
151
169
  actions: { run: (args) => handleActions(parseArgs(args), { catalog: metaMaskEngine }) },
152
170
  stop: { run: (args) => handleStop(args, stopOptions) },
@@ -155,16 +173,28 @@ const PUBLIC_RUN = {
155
173
  call: { run: (args) => withMainnetOrderAbort(targetPath(parseArgs(args).options), () => handleCall(args, callOptions)) },
156
174
  run: { run: (args) => withMainnetOrderAbort(targetPath(parseArgs(args).options), () => handleRun(args, runOptions)) },
157
175
  last: { run: (args) => handleLast(parseArgs(args)) },
158
- doctor: { run: (args) => handleDoctor(parseArgs(args)) },
176
+ doctor: {
177
+ run: (args) => {
178
+ const parsed = parseArgs(args);
179
+ const adapter = contextAdapter(targetPath(parsed.options));
180
+ if (adapter && adapterRegistry.has(adapter)) configureProviderReadiness(getAdapterSurface(adapter));
181
+ return handleDoctor(parsed, cliDoctorOptions);
182
+ }
183
+ },
159
184
  check: { run: handleCheck },
160
- "recipe-quality": { run: handleRecipeQuality },
185
+ "recipe-quality": { run: (args) => handleRecipeQuality(args, cliRecipeQualityOptions) },
161
186
  provision: { run: (args) => handleProvision(parseArgs(args)) },
162
187
  install: { run: overlay("install") },
163
188
  verify: { run: overlay("verify") },
164
189
  cleanup: { run: overlay("cleanup") },
165
190
  launch: { run: handleLaunch },
166
191
  "runtime-launch": { run: (args) => handleRuntimeLaunch(parseArgs(args)) },
167
- logs: { run: handleLogs },
192
+ logs: {
193
+ run: (args) => {
194
+ const parsed = parseArgs(args, { optionSpecs: LOGS_OPTIONS });
195
+ return handleLogs(parsed, logsOptions(parsed.options));
196
+ }
197
+ },
168
198
  debug: { run: handleDebug },
169
199
  reload: { run: handleReload },
170
200
  update: { run: handleUpdate, exit: "now", nudge: false },
package/dist/paths.js CHANGED
@@ -4,7 +4,6 @@ import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { recipeRuntimeDir, recipeRuntimePath } from "@farmslot/recipe-cli/harness";
5
5
  import { adapterRegistry } from "./adapters/registry.js";
6
6
  const runnerDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
- const PREPARE_PROGRESS_ARTIFACT = "prepare/progress.json";
8
7
  function walletFixturePath(projectRoot) {
9
8
  return recipeRuntimePath(projectRoot, "wallet-fixture.json");
10
9
  }
@@ -157,7 +156,6 @@ function isMissingPackageError(error, packageName) {
157
156
  }
158
157
  export {
159
158
  LIBRARY_ACTION_NAMESPACE,
160
- PREPARE_PROGRESS_ARTIFACT,
161
159
  assertAdapter,
162
160
  extensionIdPath,
163
161
  harnessSlotPoolDir,
@@ -3,7 +3,7 @@ import { farmslotReadyIndicator } from "./commands/farmslot-ready.js";
3
3
  import { updateStatus } from "./commands/update.js";
4
4
  import { doctorReportOptions, fixtureSummary } from "./doctor.js";
5
5
  import { loadActionManifest, validateManifest } from "./manifest.js";
6
- import { statusOptions } from "./provider-lifecycle.js";
6
+ import { cliDevicePreview, statusOptions } from "./provider-lifecycle.js";
7
7
  import { ensureRuntimeContext } from "./runtime-context.js";
8
8
  const doctorOptions = {
9
9
  ...statusOptions,
@@ -25,12 +25,14 @@ const doctorOptions = {
25
25
  userAction: (failed, retry) => failed.includes("wallet-fixture") ? `choose one wallet fixture source from nextActions, run it, then retry: ${retry}` : void 0
26
26
  }
27
27
  };
28
+ const cliDoctorOptions = { ...doctorOptions, previewDevice: cliDevicePreview };
28
29
  function configureProviderReadiness(runtime) {
29
30
  Object.assign(runtime.readiness, {
30
31
  readyIndicator: (devices) => farmslotReadyIndicator(runtime.id, [...devices])
31
32
  });
32
33
  }
33
34
  export {
35
+ cliDoctorOptions,
34
36
  configureProviderReadiness,
35
37
  doctorOptions
36
38
  };
@@ -1,14 +1,118 @@
1
- import { checkDiffSummaryReady, runCloseout, teachRecipeCompletion } from "./commands/checklist.js";
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import {
6
+ usageError
7
+ } from "@farmslot/recipe-cli/harness";
8
+ import { adapterFromOptions, harnessVersion } from "./commands/review.js";
2
9
  import {
3
10
  BASE_REVIEW_STEPS,
4
11
  REVIEW_ADAPTERS,
12
+ VERDICT_STEP,
5
13
  configPath,
14
+ discoverLibraries,
6
15
  parityAdapter,
7
16
  parityReviewSteps,
8
17
  readConfig,
9
18
  referenceHint,
19
+ resolveLibrary,
10
20
  resolveReference
11
21
  } from "./review/knowledge.js";
22
+ const require2 = createRequire(import.meta.url);
23
+ function handoffCommand() {
24
+ if (process.env.HANDOFF_BIN) {
25
+ try {
26
+ fs.accessSync(process.env.HANDOFF_BIN, fs.constants.X_OK);
27
+ return { command: process.env.HANDOFF_BIN, prefix: [] };
28
+ } catch {
29
+ return { command: process.execPath, prefix: [process.env.HANDOFF_BIN] };
30
+ }
31
+ }
32
+ return {
33
+ command: process.execPath,
34
+ prefix: [require2.resolve("@farmslot/handoff/cli")]
35
+ };
36
+ }
37
+ function closeoutArgs(taskDir, args) {
38
+ const normalized = args.flatMap((arg) => {
39
+ const option = ["--config", "--destination", "--metadata"].find(
40
+ (name) => arg.startsWith(`${name}=`)
41
+ );
42
+ return option ? [option, arg.slice(option.length + 1)] : [arg];
43
+ });
44
+ if (process.env.METAMASK_AGENTIC_LEARNINGS_DIR && !normalized.includes("--destination")) {
45
+ return [
46
+ "closeout",
47
+ taskDir,
48
+ ...normalized,
49
+ "--destination",
50
+ process.env.METAMASK_AGENTIC_LEARNINGS_DIR
51
+ ];
52
+ }
53
+ return ["closeout", taskDir, ...normalized];
54
+ }
55
+ function runCloseout(taskDir, args, stdio) {
56
+ try {
57
+ const { command, prefix } = handoffCommand();
58
+ return spawnSync(command, [...prefix, ...closeoutArgs(taskDir, args)], {
59
+ encoding: "utf8",
60
+ stdio
61
+ });
62
+ } catch (error) {
63
+ const message = error instanceof Error ? error.message : String(error);
64
+ return {
65
+ status: 1,
66
+ stdout: "",
67
+ stderr: message,
68
+ error: new Error(message)
69
+ };
70
+ }
71
+ }
72
+ function checkDiffSummaryReady(taskDir, step) {
73
+ const summaryPath = path.join(
74
+ taskDir,
75
+ "artifacts",
76
+ "validation-summary.json"
77
+ );
78
+ try {
79
+ const summary = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
80
+ if (summary.command === "check" && summary.action === "diff" && summary.status === "pass") {
81
+ return true;
82
+ }
83
+ } catch {
84
+ }
85
+ console.error(
86
+ `check-diff gate ${step} requires a task-local PASS at ${summaryPath}`
87
+ );
88
+ console.error(
89
+ `Run: mm-harness check diff [--fix] --artifacts-dir ${path.dirname(summaryPath)}`
90
+ );
91
+ console.error("Then rerun the same checklist mark command.");
92
+ return false;
93
+ }
94
+ function teachRecipeCompletion(taskDir, step) {
95
+ if (step !== "complete") return;
96
+ const artifactsDir = path.join(taskDir, "artifacts");
97
+ if (!fs.existsSync(path.join(artifactsDir, "recipe.json"))) return;
98
+ const qualityPath = path.join(artifactsDir, "recipe-quality.json");
99
+ const coveragePath = path.join(artifactsDir, "recipe-coverage.md");
100
+ const missingQuality = !fs.existsSync(qualityPath);
101
+ const missingCoverage = !fs.existsSync(coveragePath);
102
+ if (!missingQuality && !missingCoverage) return;
103
+ console.error("Recipe completion needs two closure artifacts; derive them from the real recipe run and evidence (do not stub them):");
104
+ if (missingCoverage) {
105
+ console.error(` - ${coveragePath}: map each proof target to its recipe node and evidence, or record a reasoned N/A.`);
106
+ }
107
+ if (missingQuality) {
108
+ const compactPath = path.join(artifactsDir, "recipe-quality-input.json");
109
+ console.error(" First run: mm-harness help");
110
+ console.error(` - ${qualityPath}: write ${compactPath} as:`);
111
+ console.error(' {"verdict":"pass|warn|fail","reasons":["evidence-backed reason"]}');
112
+ console.error(` then run: mm-harness recipe-quality build --input ${compactPath} --output ${qualityPath}`);
113
+ }
114
+ console.error("Then rerun the same checklist complete command.");
115
+ }
12
116
  const task = {
13
117
  stepGates: [{ label: /mm-harness check diff/u, ready: checkDiffSummaryReady }],
14
118
  closeout: runCloseout,
@@ -30,6 +134,32 @@ const review = {
30
134
  };
31
135
  }
32
136
  };
137
+ const cliReviewOptions = {
138
+ ...review,
139
+ usage: "Usage: mm-harness review checklist [--domain <name>] [--since <sha>] [--base <ref>] [--out <file>] [--json]",
140
+ domainCommand: "mm-harness domain",
141
+ defaultDomain: { read: () => process.env.MM_HARNESS_DOMAIN || void 0, label: "MM_HARNESS_DOMAIN" },
142
+ runtime: ({ action, domain }) => {
143
+ const adapter = adapterFromOptions();
144
+ if (action === "domain" && !domain && adapter === "unscoped") {
145
+ throw usageError(`No MetaMask checkout detected: pass --adapter <${REVIEW_ADAPTERS.join("|")}> or --domain <name>.`);
146
+ }
147
+ return adapter;
148
+ },
149
+ libraries: ({ target }) => discoverLibraries({ target, config: readConfig(configPath()) }).map((library) => ({
150
+ name: library.name,
151
+ root: library.root,
152
+ source: library.source,
153
+ revision: library.revision ?? null
154
+ })),
155
+ resolveLibrary: ({ domain, target }) => resolveLibrary(domain, { target, config: readConfig(configPath()), fetch: true }),
156
+ header: ({ target, library, reference, referenceMissing }) => [
157
+ `Materialized by mm-harness ${harnessVersion()} for ${target}.`,
158
+ ...library ? [`Domain knowledge: ${library.name}${library.revision ? ` @ ${library.revision}` : ""} (${library.root}, via ${library.source}).`] : [],
159
+ ...reference ? [`Parity reference: ${reference.adapter} checkout at ${reference.root} (via ${reference.source}).`] : referenceMissing ? [`Parity reference: not checked \u2014 ${referenceMissing}.`] : []
160
+ ],
161
+ verdictRule: VERDICT_STEP
162
+ };
33
163
  async function recipeAdvice(options, mode) {
34
164
  const { handleRecipeAdvice } = await import("./commands/recipe-advice.js");
35
165
  return handleRecipeAdvice(options, mode);
@@ -48,6 +178,18 @@ const recipeQuality = {
48
178
  missingAction: "Use build, plan, advise or advisor-status."
49
179
  };
50
180
  const helperCommandOptions = { task, review, recipeQuality };
181
+ const cliRecipeQualityOptions = {
182
+ subcommands: recipeQuality.subcommands,
183
+ subcommandOptions: {
184
+ names: recipeQuality.subcommandOptions.names,
185
+ message: "advice options require the advise subcommand",
186
+ userAction: "mm-harness recipe-quality advise --recipe <recipe.json> --task <TASK.md> --adapter <adapter> --advisor <provider>"
187
+ },
188
+ missingAction: "missing action: use build, plan, or advise",
189
+ usageNote: "apply the quality bar from mm-harness help, then build its compact verdict here."
190
+ };
51
191
  export {
192
+ cliRecipeQualityOptions,
193
+ cliReviewOptions,
52
194
  helperCommandOptions
53
195
  };
@@ -1,7 +1,11 @@
1
+ import {
2
+ usageError
3
+ } from "@farmslot/recipe-cli/harness";
1
4
  import { stopProjectCollector } from "../library/actions/shared/analytics/collector.mjs";
2
5
  import { collectCheckoutView, renderCheckoutView } from "./checkout-view.js";
3
6
  import { applyDeviceTargeting } from "./commands/device-target.js";
4
- import { featureFlagReport } from "./feature-flags.js";
7
+ import { doctorReportOptions } from "./doctor.js";
8
+ import { featureFlagReport, FeatureFlagSpecError, parseRemoteFlagSpec } from "./feature-flags.js";
5
9
  const statusOptions = {
6
10
  checkoutView(target, adapter) {
7
11
  const view = collectCheckoutView(target, adapter);
@@ -14,6 +18,41 @@ const statusOptions = {
14
18
  persistPin: false
15
19
  })
16
20
  };
21
+ const cliDevicePreview = (command, adapter, options) => applyDeviceTargeting(command, adapter, options, { gate: false, rerun: "" });
22
+ const cliStatusOptions = { ...statusOptions, previewDevice: cliDevicePreview, journal: false };
23
+ const prepareOptions = {
24
+ usage: "mm-harness prepare [--target <checkout>] [--platform mobile|extension|core] [--mobile-platform ios|android] [--clear-metro] [--artifacts-dir <dir>] [--json]",
25
+ launchHint: "can take minutes on Mobile: Metro bundle and app start",
26
+ steps: [{
27
+ id: "fixtures",
28
+ command: "fixtures set",
29
+ hint: "under a minute",
30
+ argv: ({ device, forwarded, targetArgs }) => [
31
+ "fixtures",
32
+ "set",
33
+ ...device.flatMap((platform) => ["--platform", platform]),
34
+ ...forwarded,
35
+ "--json",
36
+ ...targetArgs
37
+ ]
38
+ }],
39
+ deviceTarget: { option: "mobilePlatform", flag: "--mobile-platform", choices: ["ios", "android"] },
40
+ clearMetroOnly: "mobile only",
41
+ forward: {
42
+ option: "remoteFlag",
43
+ flag: "--remote-flag",
44
+ validate(value) {
45
+ try {
46
+ parseRemoteFlagSpec(value);
47
+ } catch (error) {
48
+ if (!(error instanceof FeatureFlagSpecError)) throw error;
49
+ throw usageError(`${error.message}
50
+ Next: ${error.userAction}`);
51
+ }
52
+ }
53
+ },
54
+ provenance: doctorReportOptions.provenance
55
+ };
17
56
  const stopOptions = {
18
57
  companions: async (target) => {
19
58
  const collector = await stopProjectCollector(target);
@@ -21,6 +60,9 @@ const stopOptions = {
21
60
  }
22
61
  };
23
62
  export {
63
+ cliDevicePreview,
64
+ cliStatusOptions,
65
+ prepareOptions,
24
66
  statusOptions,
25
67
  stopOptions
26
68
  };
@@ -274,74 +274,6 @@ function loadOwnedPaths(root) {
274
274
  }
275
275
  return { domain: record.domain, repos };
276
276
  }
277
- function globToRegExp(glob) {
278
- let out = "^";
279
- for (let i = 0; i < glob.length; i += 1) {
280
- const char = glob[i];
281
- if (char === "*") {
282
- if (glob[i + 1] === "*") {
283
- const slashAfter = glob[i + 2] === "/";
284
- out += slashAfter ? "(?:.*/)?" : ".*";
285
- i += slashAfter ? 2 : 1;
286
- } else {
287
- out += "[^/]*";
288
- }
289
- } else if (char === "?") {
290
- out += "[^/]";
291
- } else if (char === "[") {
292
- const negated = glob[i + 1] === "!";
293
- const bodyStart = i + 1 + (negated ? 1 : 0);
294
- const close = glob.indexOf("]", glob[bodyStart] === "]" ? bodyStart + 1 : bodyStart);
295
- if (close === -1) {
296
- out += "\\[";
297
- } else {
298
- out += `[${negated ? "^" : ""}${glob.slice(bodyStart, close).replace(/[\\\]]/g, "\\$&")}]`;
299
- i = close;
300
- }
301
- } else {
302
- out += char.replace(/[.+^${}()|\\]/g, "\\$&");
303
- }
304
- }
305
- return new RegExp(`${out}$`);
306
- }
307
- function matchOwnedFiles(files, globs) {
308
- const patterns = globs.map(globToRegExp);
309
- return files.filter((file) => patterns.some((pattern) => pattern.test(file)));
310
- }
311
- function classifyDomain(files, adapter, libraries) {
312
- const matches = [];
313
- for (const library of libraries) {
314
- const owned = loadOwnedPaths(library.root);
315
- if (!owned) continue;
316
- const matched = matchOwnedFiles(files, owned.repos[adapter] ?? []);
317
- if (matched.length > 0) matches.push({ domain: owned.domain, library, matched });
318
- }
319
- return matches.sort((a, b) => b.matched.length - a.matched.length);
320
- }
321
- function git(target, args) {
322
- return execFileSync("git", ["-C", target, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
323
- }
324
- function defaultBaseRef(target) {
325
- for (const candidate of ["origin/main", "main", "origin/develop", "develop"]) {
326
- try {
327
- git(target, ["rev-parse", "--verify", "--quiet", `${candidate}^{commit}`]);
328
- return candidate;
329
- } catch {
330
- }
331
- }
332
- return "HEAD";
333
- }
334
- function changedFiles(target, base) {
335
- const ref = base ?? defaultBaseRef(target);
336
- const lists = [
337
- ref === "HEAD" ? "" : git(target, ["diff", "--name-only", `${ref}...HEAD`]),
338
- git(target, ["diff", "--name-only", "HEAD"]),
339
- git(target, ["ls-files", "--others", "--exclude-standard"])
340
- ];
341
- const files = /* @__PURE__ */ new Set();
342
- for (const list of lists) for (const line of list.split("\n")) if (line.trim()) files.add(line.trim());
343
- return { base: ref, files: [...files].sort() };
344
- }
345
277
  function parseSections(markdown) {
346
278
  const lines = markdown.split("\n");
347
279
  const introLines = [];
@@ -457,51 +389,6 @@ function parityReviewSteps(input) {
457
389
  }
458
390
  return ["Parity: core changes are consumed by both mobile and extension; confirm both consumers still compile against the public surface."];
459
391
  }
460
- function renderReviewChecklist(input) {
461
- const diffRange = input.since ? `${input.since}..HEAD` : `${input.base}...HEAD`;
462
- const lines = [];
463
- const scope = input.domain ? `${input.domain} (${input.adapter})` : input.adapter;
464
- lines.push(`# Review checklist \u2014 ${scope}`, "");
465
- lines.push(`Materialized by mm-harness ${input.harnessVersion} for ${input.target}.`);
466
- if (input.knowledge) {
467
- const { library } = input.knowledge;
468
- lines.push(`Domain knowledge: ${library.name}${library.revision ? ` @ ${library.revision}` : ""} (${library.root}, via ${library.source}).`);
469
- }
470
- if (input.reference) {
471
- lines.push(`Parity reference: ${input.reference.adapter} checkout at ${input.reference.root} (via ${input.reference.source}).`);
472
- } else if (input.referenceMissing) {
473
- lines.push(`Parity reference: not checked \u2014 ${input.referenceMissing}.`);
474
- }
475
- lines.push(`Scope: \`git diff ${diffRange}\`${input.since ? " (incremental re-review since the last verdict)" : ""}.`, "");
476
- let step = 0;
477
- const item = (text) => {
478
- step += 1;
479
- lines.push(`- [ ] ${step}. ${text}`);
480
- };
481
- lines.push("## Setup");
482
- if (input.since) {
483
- item(`Re-read the previous findings, then list only the files changed in \`git diff --name-only ${diffRange}\`; confirm each earlier finding is addressed or explicitly declined.`);
484
- } else {
485
- item(`Read the task or PR description and the linked ticket, then list the changed files with \`git diff --name-only ${diffRange}\`.`);
486
- }
487
- item(
488
- input.domain ? `Confirm the domain is ${input.domain} (\`mm-harness domain\`) and open ${input.knowledge?.antipatterns ? "review/antipatterns.md" : "the domain library"} once before reading the diff.` : "Run `mm-harness domain` to check whether a team library owns the changed files; if one does, re-run with --domain."
489
- );
490
- lines.push("", "## Base review");
491
- for (const text of BASE_REVIEW_STEPS) item(text);
492
- if (input.knowledge?.antipatterns) {
493
- lines.push("", `## Domain patterns (${input.domain})`);
494
- for (const section of input.knowledge.antipatterns.sections) {
495
- item(`${section.title}: ${section.summary}`);
496
- }
497
- }
498
- lines.push("", "## Parity");
499
- for (const step2 of parityReviewSteps({ ...input, hasParityGuide: Boolean(input.knowledge?.parity) })) item(step2);
500
- lines.push("", "## Verdict");
501
- item(VERDICT_STEP);
502
- return `${lines.join("\n")}
503
- `;
504
- }
505
392
  function renderReviewHelp(input) {
506
393
  const lines = [];
507
394
  lines.push(`mm-harness ${input.harnessVersion} review guide${input.domain ? ` \u2014 ${input.domain}` : ""}`, "");
@@ -542,21 +429,16 @@ export {
542
429
  REVIEW_ADAPTERS,
543
430
  SKILLS_CACHE_DIR,
544
431
  VERDICT_STEP,
545
- changedFiles,
546
- classifyDomain,
547
432
  configPath,
548
433
  declaredLibraryPathEnv,
549
- defaultBaseRef,
550
434
  discoverLibraries,
551
435
  excludeCacheDir,
552
436
  fetchLibrary,
553
437
  gitExcludeFile,
554
- globToRegExp,
555
438
  hydrateLibraryPathEnv,
556
439
  libraryCachePath,
557
440
  loadDomainKnowledge,
558
441
  loadOwnedPaths,
559
- matchOwnedFiles,
560
442
  parityAdapter,
561
443
  parityReviewSteps,
562
444
  parseAdvisorSettings,
@@ -565,7 +447,6 @@ export {
565
447
  readConfig,
566
448
  referenceEnvName,
567
449
  referenceHint,
568
- renderReviewChecklist,
569
450
  renderReviewHelp,
570
451
  resolveLibrary,
571
452
  resolveReference,
@@ -88,8 +88,9 @@ command-local platform ladders for surface-owned behavior:
88
88
  - the runner reads `actions` (manifest, bundled semantic actions, live adapter
89
89
  scripts, `cdp.target`, the `ui.*` transport, adapter wrappers, input checks);
90
90
  - `install`/`verify`/`cleanup` read `harness`, `agentic-runtime.json` reads
91
- `runtimeContext`, `doctor`/`status` read `readiness`, and `fixtures`,
92
- `prepare`, `debug`, `reload` and `runtime-decision` read their own members.
91
+ `runtimeContext`, `doctor`/`status` read `readiness`, `prepare` reads
92
+ `readiness.prepare`, and `fixtures`, `debug`, `reload` and
93
+ `runtime-decision` read their own members.
93
94
 
94
95
  A few comparisons remain where a command exists for one platform
95
96
  (`resolve-extension`, `ensure-ready`, `runtime-launch`) or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.92.21",
3
+ "version": "0.92.23",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -24,11 +24,11 @@
24
24
  "@farmslot/adapter-node": "0.1.34",
25
25
  "@farmslot/adapter-rn": "0.22.23",
26
26
  "@farmslot/adapter-sdk": "0.18.0",
27
- "@farmslot/adapter-web": "0.10.14",
27
+ "@farmslot/adapter-web": "0.10.15",
28
28
  "@farmslot/agent-runtime": "0.29.0",
29
29
  "@farmslot/handoff": "^0.3.1",
30
30
  "@farmslot/protocol": "0.54.0",
31
- "@farmslot/recipe-cli": "0.23.0",
31
+ "@farmslot/recipe-cli": "0.23.1",
32
32
  "@farmslot/recipe-runner": "0.39.0",
33
33
  "agent-device": "0.19.3",
34
34
  "commander": "^12.0.0",