@deeeed/metamask-harness 0.3.7 → 0.3.9

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.
@@ -7,11 +7,13 @@
7
7
  // teach; genuinely-unknown commands fall to commander's default.
8
8
 
9
9
  import { spawnSync } from 'node:child_process';
10
+ import fs from 'node:fs';
10
11
  import path from 'node:path';
11
12
  import { fileURLToPath } from 'node:url';
12
13
 
13
14
  import { Command } from 'commander';
14
15
 
16
+ import { color } from './cli-color.ts';
15
17
  import { handleUpdate, maybeNudge } from './commands/update.ts';
16
18
 
17
19
  // …/src → its parent is the package root that holds scripts/.
@@ -57,6 +59,25 @@ Example:
57
59
  mm-harness actions --adapter mobile
58
60
  mm-harness actions --adapter extension --raw`,
59
61
  },
62
+ {
63
+ name: 'stop',
64
+ summary: 'Stop the dev server this checkout owns (mobile: port-scoped Metro) and close its log window.',
65
+ example: 'mm-harness stop',
66
+ helpText: `mm-harness stop [flags]
67
+
68
+ Stop the Metro dev server this checkout owns and close its tmux log-tail window.
69
+ Port-scoped: only the listener on the checkout's resolved port is signalled, so
70
+ concurrent slots are untouched. Nothing running = success (idempotent).
71
+ Extension/core: teaching error (their dev servers are slot-managed).
72
+
73
+ --port <port> Metro port (default: the checkout's slot context)
74
+ --target <path> Checkout path (default: cwd)
75
+ --json Machine-readable output
76
+
77
+ Example:
78
+ mm-harness stop
79
+ mm-harness stop --port 8061`,
80
+ },
60
81
  {
61
82
  name: 'call',
62
83
  summary: 'Run one action in isolation as a one-node recipe through the real engine path (fuzzy short names; --arg k=v; same trace/evidence as run).',
@@ -345,7 +366,7 @@ const HELP_GROUPS: HelpGroup[] = [
345
366
  {
346
367
  title: 'DAILY LOOP',
347
368
  blurb: 'what a teammate runs many times a day (auto-ensures the overlay; --heal owns recovery)',
348
- commands: ['launch', 'logs', 'debug', 'fixtures'],
369
+ commands: ['launch', 'stop', 'logs', 'debug', 'fixtures'],
349
370
  },
350
371
  {
351
372
  title: 'DISCOVER',
@@ -375,19 +396,51 @@ function commandMeta(name: string): { summary: string; example: string; planned:
375
396
  return { summary: '', example: '', planned: false };
376
397
  }
377
398
 
399
+ // Slot context the orchestrator's prepare wrote into the checkout, when the
400
+ // help is run from inside one. Presence-gated: no context, no line.
401
+ function detectedSlotLine(out: (style: string, text: string) => string): string | null {
402
+ const ctxPath = path.join(
403
+ process.cwd(),
404
+ process.env.RECIPE_RUNTIME_DIR || 'temp/recipe/runtime',
405
+ 'agentic-runtime.json',
406
+ );
407
+ try {
408
+ const ctx = JSON.parse(fs.readFileSync(ctxPath, 'utf8')) as Record<string, unknown>;
409
+ const parts: string[] = [];
410
+ if (ctx.slotId) parts.push(`slot ${out('ok', String(ctx.slotId))}`);
411
+ if (ctx.simulator) parts.push(`device ${out('ok', String(ctx.simulator))}`);
412
+ if (ctx.metroPort) parts.push(`metro :${out('ok', String(ctx.metroPort))}`);
413
+ if (ctx.gitBranch) parts.push(`branch ${out('info', String(ctx.gitBranch))}`);
414
+ if (parts.length === 0) return null;
415
+ return `${out('label', 'SLOT')} — this checkout is a prepared slot: ${parts.join(' · ')}`;
416
+ } catch {
417
+ return null;
418
+ }
419
+ }
420
+
378
421
  function groupedHelp(): string {
422
+ const out = (style: string, text: string) => color(style, text, { stream: process.stdout });
379
423
  const lines: string[] = [];
380
- lines.push('mm-harness — one front door for the MetaMask recipe loop: launch the app, prove behavior, manage the runtime overlay.');
424
+ lines.push(`${out('bold', 'mm-harness')} — one front door for the MetaMask recipe loop: launch the app, prove behavior, manage the runtime overlay.`);
381
425
  lines.push('Run it from inside a MetaMask checkout; the platform (mobile | extension | core) is auto-detected.');
382
- lines.push('Grammar: mm-harness <command> [target] [flags] (target is a positional: ios | android; flags add agent depth; --json is the agent contract)');
426
+ lines.push(`Grammar: ${out('cmd', 'mm-harness <command> [target] [flags]')} (target is a positional: ios | android; flags add agent depth; --json is the agent contract)`);
427
+ if (process.env.MM_HARNESS_BIN) {
428
+ lines.push('');
429
+ lines.push(`${out('warn', 'DEV OVERRIDE ACTIVE')} — this run is served by MM_HARNESS_BIN=${out('path', process.env.MM_HARNESS_BIN)} (unset it to return to the installed/global bin).`);
430
+ }
431
+ const slotLine = detectedSlotLine(out);
432
+ if (slotLine) {
433
+ lines.push('');
434
+ lines.push(slotLine);
435
+ }
383
436
  for (const group of HELP_GROUPS) {
384
437
  lines.push('');
385
- lines.push(`${group.title} — ${group.blurb}:`);
438
+ lines.push(`${out('label', group.title)} — ${group.blurb}:`);
386
439
  for (const name of group.commands) {
387
440
  const meta = commandMeta(name);
388
441
  const planned = meta.planned ? ' (planned)' : '';
389
- lines.push(` ${name.padEnd(10)} ${meta.summary}${planned}`);
390
- lines.push(` ${meta.example}`);
442
+ lines.push(` ${out('cmd', name.padEnd(10))} ${meta.summary}${planned}`);
443
+ lines.push(` ${out('comment', meta.example)}`);
391
444
  }
392
445
  }
393
446
  lines.push('');