@deeeed/metamask-harness 0.92.22 → 0.92.24
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 +43 -0
- package/dist/adapters/core/surface.js +2 -3
- package/dist/adapters/extension/surface.js +1 -2
- package/dist/adapters/mobile/devices.js +0 -1
- package/dist/adapters/mobile/overlay.js +3 -2
- package/dist/adapters/mobile/prepare-target.js +13 -0
- package/dist/adapters/mobile/surface.js +1 -2
- package/dist/commands/doctor.js +3 -423
- package/dist/commands/review.js +5 -66
- package/dist/doctor.js +0 -11
- package/dist/mm-harness-cli.js +50 -20
- package/dist/paths.js +0 -2
- package/dist/provider-doctor.js +3 -1
- package/dist/provider-helpers.js +143 -1
- package/dist/provider-lifecycle.js +43 -1
- package/dist/review/knowledge.js +0 -119
- package/docs/CONTRIBUTING.md +3 -2
- package/library/actions/mobile/perps/perps.mjs +33 -3
- package/package.json +3 -3
- package/dist/commands/checklist.js +0 -242
- package/dist/commands/domain.js +0 -57
- package/dist/commands/execution-template.js +0 -28
- package/dist/commands/logs.js +0 -99
- package/dist/commands/prepare.js +0 -433
- package/dist/commands/recipe-quality.js +0 -103
- package/dist/commands/status-watch.js +0 -317
- package/dist/commands/status.js +0 -146
- package/dist/commands/task-init.js +0 -49
- package/dist/task-view.js +0 -332
package/dist/commands/review.js
CHANGED
|
@@ -9,11 +9,9 @@ import {
|
|
|
9
9
|
readConfig,
|
|
10
10
|
referenceHint,
|
|
11
11
|
REVIEW_ADAPTERS,
|
|
12
|
-
renderReviewChecklist,
|
|
13
12
|
renderReviewHelp,
|
|
14
13
|
resolveLibrary,
|
|
15
|
-
resolveReference
|
|
16
|
-
defaultBaseRef
|
|
14
|
+
resolveReference
|
|
17
15
|
} from "../review/knowledge.js";
|
|
18
16
|
const EXIT_OK = 0;
|
|
19
17
|
let cachedHarnessVersion;
|
|
@@ -32,16 +30,15 @@ function adapterFromOptions() {
|
|
|
32
30
|
}
|
|
33
31
|
return scope ?? "unscoped";
|
|
34
32
|
}
|
|
35
|
-
function resolveReviewContext(options
|
|
33
|
+
function resolveReviewContext(options) {
|
|
36
34
|
const target = targetPath(options);
|
|
37
35
|
const adapter = adapterFromOptions();
|
|
38
36
|
const domain = optionString(options, "domain") ?? process.env.MM_HARNESS_DOMAIN;
|
|
39
37
|
const config = readConfig(configPath());
|
|
40
38
|
const context = { target, adapter, ...domain ? { domain } : {} };
|
|
41
39
|
if (domain) {
|
|
42
|
-
const library = resolveLibrary(domain, { target, config
|
|
40
|
+
const library = resolveLibrary(domain, { target, config });
|
|
43
41
|
if (library) context.knowledge = loadDomainKnowledge(library, adapter === "unscoped" ? void 0 : adapter);
|
|
44
|
-
else if (fetch) throw usageError(`No library found for domain "${domain}". Set RECIPE_LIBRARY_PATH="${domain}=<path>" or run: mm-harness config set libraries.${domain} <path>`);
|
|
45
42
|
}
|
|
46
43
|
const parity = parityAdapter(adapter);
|
|
47
44
|
if (parity) {
|
|
@@ -53,7 +50,7 @@ function resolveReviewContext(options, { fetch = true } = {}) {
|
|
|
53
50
|
}
|
|
54
51
|
function handleHelpReview(argv, harnessVersion2) {
|
|
55
52
|
const parsed = parseArgs(argv);
|
|
56
|
-
const context = resolveReviewContext(parsed.options
|
|
53
|
+
const context = resolveReviewContext(parsed.options);
|
|
57
54
|
const input = {
|
|
58
55
|
adapter: context.adapter,
|
|
59
56
|
harnessVersion: harnessVersion2,
|
|
@@ -70,66 +67,8 @@ function handleHelpReview(argv, harnessVersion2) {
|
|
|
70
67
|
}
|
|
71
68
|
return EXIT_OK;
|
|
72
69
|
}
|
|
73
|
-
async function handleReview(argv, version = harnessVersion()) {
|
|
74
|
-
const parsed = parseArgs(argv);
|
|
75
|
-
const subcommand = parsed.positional[0];
|
|
76
|
-
if (subcommand !== "checklist") {
|
|
77
|
-
throw usageError("Usage: mm-harness review checklist [--domain <name>] [--since <sha>] [--base <ref>] [--out <file>] [--json]");
|
|
78
|
-
}
|
|
79
|
-
const context = resolveReviewContext(parsed.options);
|
|
80
|
-
const since = optionString(parsed.options, "since");
|
|
81
|
-
const base = optionString(parsed.options, "base") ?? defaultBaseRef(context.target);
|
|
82
|
-
const checklist = renderReviewChecklist({
|
|
83
|
-
adapter: context.adapter,
|
|
84
|
-
target: context.target,
|
|
85
|
-
base,
|
|
86
|
-
harnessVersion: version,
|
|
87
|
-
...since ? { since } : {},
|
|
88
|
-
...context.domain ? { domain: context.domain } : {},
|
|
89
|
-
...context.knowledge ? { knowledge: context.knowledge } : {},
|
|
90
|
-
...context.reference ? { reference: context.reference } : {},
|
|
91
|
-
...context.referenceMissing ? { referenceMissing: context.referenceMissing } : {}
|
|
92
|
-
});
|
|
93
|
-
const out = optionString(parsed.options, "out");
|
|
94
|
-
if (out) {
|
|
95
|
-
const file = path.resolve(out);
|
|
96
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
97
|
-
fs.writeFileSync(file, checklist);
|
|
98
|
-
}
|
|
99
|
-
if (optionFlag(parsed.options, "json")) {
|
|
100
|
-
process.stdout.write(
|
|
101
|
-
`${JSON.stringify(
|
|
102
|
-
{
|
|
103
|
-
command: "review",
|
|
104
|
-
subcommand: "checklist",
|
|
105
|
-
target: context.target,
|
|
106
|
-
adapter: context.adapter,
|
|
107
|
-
domain: context.domain ?? null,
|
|
108
|
-
library: context.knowledge?.library ?? null,
|
|
109
|
-
reference: context.reference ?? null,
|
|
110
|
-
referenceMissing: context.referenceMissing ?? null,
|
|
111
|
-
base,
|
|
112
|
-
since: since ?? null,
|
|
113
|
-
out: out ? path.resolve(out) : null,
|
|
114
|
-
checklist
|
|
115
|
-
},
|
|
116
|
-
null,
|
|
117
|
-
2
|
|
118
|
-
)}
|
|
119
|
-
`
|
|
120
|
-
);
|
|
121
|
-
} else if (out) {
|
|
122
|
-
process.stdout.write(`review checklist: ${path.resolve(out)}
|
|
123
|
-
`);
|
|
124
|
-
} else {
|
|
125
|
-
process.stdout.write(checklist);
|
|
126
|
-
}
|
|
127
|
-
return EXIT_OK;
|
|
128
|
-
}
|
|
129
70
|
export {
|
|
130
71
|
adapterFromOptions,
|
|
131
72
|
handleHelpReview,
|
|
132
|
-
|
|
133
|
-
harnessVersion,
|
|
134
|
-
resolveReviewContext
|
|
73
|
+
harnessVersion
|
|
135
74
|
};
|
package/dist/doctor.js
CHANGED
|
@@ -5,16 +5,6 @@ import { normalizeFeatureFlagRecord } from "./feature-flags.js";
|
|
|
5
5
|
import { getAdapterSurface } from "./adapters/surface.js";
|
|
6
6
|
import { isPin } from "./harness-pin.js";
|
|
7
7
|
import { readJson } from "./paths.js";
|
|
8
|
-
function requiredDoctorCheckSummary(checks) {
|
|
9
|
-
const required = checks.filter((check) => check.required);
|
|
10
|
-
const failed = required.filter((check) => check.status === "fail").map((check) => check.id);
|
|
11
|
-
return {
|
|
12
|
-
status: failed.length === 0 ? "pass" : "fail",
|
|
13
|
-
total: required.length,
|
|
14
|
-
passed: required.length - failed.length,
|
|
15
|
-
failed
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
8
|
function repoShape(target) {
|
|
19
9
|
const exists = (rel) => fs.existsSync(path.join(target, rel));
|
|
20
10
|
const packageInfo = readPackageInfo(target);
|
|
@@ -188,7 +178,6 @@ export {
|
|
|
188
178
|
fixtureSummary,
|
|
189
179
|
renderRuntimeContext,
|
|
190
180
|
repoShape,
|
|
191
|
-
requiredDoctorCheckSummary,
|
|
192
181
|
runnerInstallKind,
|
|
193
182
|
runnerProvenance,
|
|
194
183
|
runtimeContextSummary
|
package/dist/mm-harness-cli.js
CHANGED
|
@@ -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 {
|
|
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:
|
|
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: {
|
|
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: {
|
|
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,
|
package/dist/provider-doctor.js
CHANGED
|
@@ -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
|
};
|
package/dist/provider-helpers.js
CHANGED
|
@@ -1,14 +1,118 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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
|
};
|