JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 421
  • Score
    100M100P100Q96018F
  • License (MIT OR Apache-2.0)

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@metamask/agentic-cli) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    @metamask/agentic-cli

    MetaMask Agentic CLI (mm) — a dual-mode CLI for interacting with the MetaMask Agentic platform. Supports both standard single-command execution and an interactive REPL shell.

    Prerequisites

    • Node.js >= 22.x
    • npm >= 10.x

    Running Locally

    1. Install dependencies

    From the repo root:

    npm install

    2. Build

    Build all packages (from repo root):

    npm run build

    Or build only the CLI package:

    npm run build:local -w packages/agentic-cli

    3. Run the CLI

    After building, run directly with Node:

    node packages/agentic-cli/dist/lib.esm/index.js

    Or link globally for the mm command:

    npm link -w packages/agentic-cli
    mm --help

    4. Development mode (watch)

    Start a dev watcher that rebuilds on file changes:

    npm run dev -w packages/agentic-cli

    Or start all packages in parallel from the repo root:

    npm run dev

    Available Commands

    Command Description
    mm init Initialize project settings
    mm login Sign in (QR, browser/social, or token)
    mm auth status Check authentication status
    mm reset Reset session / configuration

    Global Flags

    Flag Description
    -f, --format <fmt> stdout format for headless runs: json, text, yaml, toml, toon
    --json shorthand for --format=json (also forces headless on a TTY, like -f json)
    --toon shorthand for --format=toon (also forces headless on a TTY, like -f toon)

    When multiple format flags are combined (e.g. --json --format toon), the latest token on the command line wins. | -v, --verbose | Enables ctx.logger and stderr output from io.log / io.progress (see Logging and progress). |

    REPL vs headless

    • REPL — If stdin and stdout are both a TTY and you run mm with no arguments, or mm init without -f / --format, --json, or --toon, the CLI starts the interactive Ink shell (history, ReplCommandRunner per line).
    • Headless — All other invocations run a single oclif command. The headless renderer drives execute: there is no interactive ask. Use flags, env vars, and schema default values so io.resolveInputs never needs a prompt; otherwise required fields raise MISSING_FLAG. For prompts, use the REPL or pass everything explicitly.

    Init Flow

    mm init configures your local project settings. It walks you through a series of prompts and persists choices to ~/.metamask/session.json. By default the CLI uses a server wallet — pass --with-private-key to bring your own keys instead.

    Step by step

    1. Run the command

      mm init

      On a TTY, plain mm init (no --format / -f) starts the REPL and runs init as the first input line. Run mm alone to open the shell with an empty first line.

      To force headless init with prompts disabled, pass a format, e.g. mm init -f json or mm init --toon (then provide any required values via flags or env).

    2. Done — settings are saved with server-wallet mode and the CLI prints ✔ Project initialized

    Bring your own keys (BYOK)

    Use the REPL (mm on a TTY, then init --with-private-key) so io.resolveInputs can prompt for keys (EVM / Solana are optional — press Enter to skip), or pass everything on the CLI for headless runs:

    mm init --with-private-key

    This sets wallet mode to byok and stores the provided keys in the session.

    Optional mnemonic password (BYOK)

    Encrypt the stored mnemonic with a password at init:

    mm init --wallet byo --mnemonic "<phrase>" --password "<pw>"
    # or: MM_PASSWORD=<pw> mm init --wallet byo --mnemonic "<phrase>"

    Manage the password later:

    mm wallet password set    --new=<pw>
    mm wallet password change --current=<old> --new=<new>
    mm wallet password remove --current=<pw>

    Unlock for a single headless BYOK command that signs or derives keys with --password or MM_PASSWORD. Commands that do not unlock the stored mnemonic do not accept this flag. Plaintext mnemonics written before this feature keep working unchanged.

    Non-interactive usage

    Pass BYOK values as flags to skip prompts:

    mm init \
      --with-private-key \
      --evm-private-key 0xYOUR_KEY

    View current settings

    mm init show

    Login and auth status

    mm login

    On a TTY, run mm login (or open the REPL with mm and type login). Choose QR (MetaMask Mobile), browser (Google or email via dashboard), or token (paste an existing auth token). Session fields used by authenticated commands are updated when sign-in completes.

    mm login social          # open dashboard + MWP pairing code
    mm login --mode social   # same

    Set MM_DASHBOARD_URL to override the dashboard base URL (default https://test-dashboard.web3auth.io for mm-dev, https://developer.metamask.io for mm). See mm login --help for other flags.

    mm auth status

    mm auth status

    This reports whether you are authenticated, your project ID, wallet address, and token expiry status.

    Session Storage

    All session data is persisted to:

    ~/.metamask/session.json

    The file is created with restricted permissions (0600). Use mm reset to clear it.

    Adding a new command

    Commands are oclif classes that extend Command from src/runtime/Command.ts. The base class wires init() (context, session, flags), run() (headless output), and optional auth in beforeExecute. You implement execute(io) and return plain data; renderers turn that into stdout (headless) or Ink (REPL).

    1. Create a command module under src/commands/, e.g. src/commands/foo/FooCommand.ts. Optionally add view.tsx for the success Ink tree.

    2. Set static metadata on the class:

      • static override requiresAuth = true | false — when true, beforeExecute checks / refreshes MM auth (see Command.beforeExecute).
      • static override description = "..." — short help text.
      • static override flags — usually { ...Command.baseFlags } or { ...Command.flagsWithInputs(yourSchema) } (see Configuring input fields).
      • static View — optional React component for REPL success UI: ({ data }: FinalProps<TFinal>) => …. Omit it if you only care about serialized headless output.
    3. Optional streaming UI

      • Set static LiveView to a component receiving LiveProps (items, progress, emitted, signal) while execute runs.
      • From execute, call io.yield(item) to append typed rows (NDJSON on stdout in headless mode when any item was yielded). Use io.emit(<Ink node>) for one-off lines in the REPL history.
    4. Implement async execute(io: CommandIO): Promise<TFinal>

      • Read this.ctx.flags, this.ctx.session, this.ctx.authService as needed.
      • Resolve inputs with await io.resolveInputs(schema) (never this.resolveInputs).
      • Throw ValidationError for invalid flags/inputs (new ValidationError("CODE", "message", "hint")), or CommandError for other expected failures.
    5. Register the command in src/commands/index.ts:

      export const commands = {
        // ...
        foo: FooCommand, // → `mm foo`
        "topic:bar": BarCommand, // → `mm topic bar`
      };
    6. Buildnpm run build -w packages/agentic-cli (or root npm run build). oclif loads commands from this registry.

    Minimal example (no extra input schema, no auth, with a simple View):

    import { Text } from "ink";
    
    import { Command } from "../../runtime/Command";
    import type { CommandIO, FinalProps } from "../../runtime/CommandIO";
    
    type FooData = { ok: true };
    
    function FooView({ data }: FinalProps<FooData>) {
      return <Text>{data.ok ? "ok" : ""}</Text>;
    }
    
    export class FooCommand extends Command<FooData> {
      static override requiresAuth = false;
      static override description = "Example";
      static override flags = { ...Command.baseFlags };
      static override View = FooView;
    
      async execute(_io: CommandIO): Promise<FooData> {
        return { ok: true };
      }
    }

    Logging and progress

    There are two related surfaces: session-wide logger on the context, and CommandIO methods used inside execute.

    this.ctx.logger

    • Type: Logger (debug, info, warn, error).
    • Disabled by default (no-ops). Command.init calls logger.enable(this) when the user passes --verbose, wiring methods to oclif’s stderr logging.
    • Use this.ctx.logger in beforeExecute, execute, or afterExecute for implementation diagnostics (auth checks, HTTP traces, etc.) that should respect the global verbose flag once.

    io.log and io.progress

    Defined on CommandIO and passed into execute by the active renderer:

    Method Purpose
    io.progress(label?) Single transient status line while work runs. In the REPL, updates the spinner area in ReplCommandRunner. In headless mode, a line is written to stderr only when --verbose: [progress] …. Pass undefined to clear.
    io.log(level, msg) Structured lines to stderr when --verbose: [level] ….

    The default beforeExecute (when requiresAuth is true) already uses io.progress and logger.debug around token validation; follow the same pattern for long steps instead of a separate “loader” type.

    Note: There is no getLoader() / Loader interface anymore — use io.progress (and optional io.emit / LiveView) for user-visible activity.

    Configuring input fields

    Structured CLI flags and prompts are driven by an InputSchema: a plain object map of field name → InputField, consumed by schemaToFlags and resolveInputs in src/helpers/input.ts. Merge into command flags with Command.flagsWithInputs(schema), then in execute:

    const inputs = await io.resolveInputs(mySchema);

    resolveInputs reads this.ctx.flags (via the flags object the renderer passes into io; same values oclif parsed). In the REPL, the interactive renderer supplies io.ask so missing values can be prompted. In headless mode the asker is null, so only flags, env, and defaults apply.

    Common properties (all field types)

    Property Purpose
    flag oclif flag name (kebab-case), e.g. with-private-key--with-private-key.
    message Prompt text and help description.
    env Optional env var fallback.
    required If not false, missing value (no flag, no env, no prompt) throws MISSING_FLAG.
    prompt If false, never prompt; value must come from flag or env.
    index Sort order for resolution. Lower runs first — required when using when so dependencies exist before conditional fields.
    when (resolved) => boolean — if false, field is skipped (not read, not prompted).
    parse / validate Normalize and validate after flag or prompt.

    Field types

    type CLI flag Interactive prompt
    select string (constrained options) list select
    text string text input
    password string masked input
    confirm string yes/no confirm
    boolean Flags.boolean (no value after flag) confirm when prompt is not false and flag omitted

    Boolean quirks

    • Fields with prompt: false (e.g. --with-private-key) use an oclif default so omitted flags have a defined value.
    • Fields that should prompt when omitted must not use an oclif default on the flag; the schema omits default on Flags.boolean in that case so “not passed” stays undefined and resolveInputs can show the confirm. The schema’s own default is still used when there is no prompter (fully non-interactive run).

    For a full example, see src/commands/init/inputs.ts (when + index for BYOK keys after withPrivateKey).

    Tests

    npm test -w packages/agentic-cli

    Tests use Vitest. Unit specs live under test/unit-test/; integration tests under test/integration-test/.