Skip to content

CLI reference

Running mj without a subcommand starts the per-user daemon when necessary and opens the terminal dashboard. If no workspace exists, it creates one using the current directory name and leaves it ready for an explicit new session. Otherwise it opens the requested workspace or the most recently opened workspace. The terminal surface needs at least 60 columns, and 80 for the Sessions sidebar to sit beside the conversation.

mj [--instance <name>] [--workspace <name>] [command]

mj --workspace <name> with no command opens the dashboard in that workspace. Given before a command that works in a workspace, it applies to that command. It is not a global option: the commands that work in a workspace (mj new, mj acp, mj sessions, mj events, mj resume, and mj import) take --workspace <name> after the command name too, and the other commands refuse it there. mj new and mj acp require it. mj sessions and mj events show only that workspace when it is given, and every workspace when it is not. mj import, and mj resume --wiki when it imports another tool’s session, put the session in it and need it when the instance has more than one workspace.

--instance (-i, or MJ_INSTANCE) is global and runs a fully isolated copy — configuration, database, daemon, and logs under instances/<name> (for example mj -i dev daemon status). Hidden worker, broker, daemon-run, and desktop-bootstrap commands are internal implementation interfaces and are intentionally omitted here.

mj acp is listed in mj --help. It is the ACP agent a program starts instead of a coding harness, and is documented in ACP agent.

CommandPurpose
mjOpen or attach to the terminal dashboard.
mj --workspace <name>Open a particular workspace.
mj workspacesOpen the workspace manager in the dashboard.
mj workspaces list [--json]List workspaces without a terminal.
mj workspaces create <name> [--json]Create a workspace, or select the one that already has the name.
mj go [--setup] [--global-default] [<folder>]Open the dashboard on a folder (default: the current directory) with its remembered account and target. --setup changes that folder’s setup; --global-default also makes it the default for new projects.
mj simple [<folder>]Open the folder’s last running Simple session or start one immediately with the default enabled agent, the local localhost target, and an isolated checkout. Commands are direct Ctrl-key chords with no prefix, so Ctrl+B remains available to tmux.
mj appOpen the authenticated web viewer in the separate mj-desktop application.

Use prefix+q to detach from the dashboard without stopping the daemon or any session. The terminal surface documents its keys; the web viewer covers mj app and browser access.

mj simple is intentionally narrower than mj go: it does not show the profile, target, project, or review wizard, does not choose a remote or container target, and does not write its fixed recipe into the folder’s normal mj go setup. In Simple mode, ctrl+q detaches, ctrl+n starts another session, ctrl+g opens the session list, and ctrl+f1 lists every command. Ctrl+B is not a Mjolnir prefix or command.

mj daemon status
mj daemon stop
mj daemon restart
  • status prints the daemon PID, version, start time, connected-client count, and viewer state. When the viewer is ready it also prints the URL and six-digit access code; otherwise it reports disabled, starting, stopped, or error state.
  • stop gracefully stops the controller daemon. Detached workers keep running.
  • restart gracefully replaces the daemon with the installed Mjolnir build and reconnects to existing workers. It refuses when the running daemon’s build is newer than its own; to go back to an older build, run mj daemon stop first.
mj setup
mj setup instructions --platform linux
mj setup instructions --platform macos

mj setup reruns the same automatic agent and repository discovery used on first interactive startup. It detects installed harnesses and home-directory overrides, adds profiles and a bundle for the current GitHub origin, and preserves existing entries and preferences. Matching entries are reused; identifier collisions get separate names. It also probes every SSH or EC2 machine and adds each runtime kind it newly runs; a runtime switched off with enabled = false is never added back. It saves additions without questions and reports actionable doctor errors. Container downloads remain background daemon work. Configure container options, SSH hosts, and AWS targets in Settings; use mj doctor --smoke for explicit container smoke tests.

setup instructions prints coding-agent-friendly preparation steps for a Linux or macOS host.

mj doctor
mj doctor --json
mj doctor --smoke

Doctor checks configuration compatibility, harness authentication, worker binaries, container runtimes, SSH targets, AWS prerequisites, and Apple Container support. Results are classified as ready, warning, fixable, or unsupported.

--json emits a machine-readable array. --smoke adds real disposable pull/run probes where a target supports them; run it after resolving ordinary fixable checks because it can download an image and provision short-lived resources.

mj login
mj login --profile <profile-id>
mj login --profile <profile-id> --method <acp-method-id>
mj login --profile <profile-id> --cli
mj login --profile <claude-profile> --setup-token

The profile may be omitted only when exactly one is configured. Mjolnir installs or reuses the same pinned harness a session uses, starts it as an ACP agent, and asks it which authentication methods it advertises. It then runs a terminal method’s command, uses a known terminal action for a bridge that describes one in text only (OpenCode), or completes an agent method through ACP authenticate (Codex). Use --method to choose when several methods are offered, or --cli to skip ACP and run the harness command found on PATH exactly as earlier releases did. If the harness advertises no ACP login, Mjolnir also falls back to that command-line login. A profile pointed at your own OpenCode or Muse home signs in through that installed CLI, so the pinned binary never runs against a login database it does not own. The same flow is available from the dashboard: Settings → Agent Profiles, select a profile, then Sign in. A profile that authenticates with an API key, such as a DSH profile or a Codex profile with a custom provider, has no login, and mj login refuses it. Native Windows controllers take the CLI login path; ACP sign-in is available on Unix (including WSL). --setup-token is Claude-only: it mints and stores a long-lived subscription token so containers do not race the controller for a rotating OAuth refresh token.

See profiles and harnesses for home directories, credential handling, and runtime limitations.

mj github-token --owner <login>
mj github-token --repo <owner>/<repository> [--repo <owner>/<repository> ...]

Print a currently valid installation token for the configured GitHub App. The daemon owns the shared token cache; this command does not need the private key on the caller’s host. --owner returns an installation-wide token. One or more --repo options discover an installation and limit the token to those repositories; all selected repositories must belong to the same installation. The command fails clearly when no GitHub App is configured. Treat its output as a secret. See GitHub App credentials for setup and CI usage.

mj import <harness> (--session <uuid> | --latest) [options]

<harness> is one of claude, codex, kimi, grok, or muse. OpenCode keeps its sessions in a SQLite store and is not importable yet; DSH sessions are not importable yet either.

OptionMeaning
--session <uuid>Import one native session by its harness ID. Mutually exclusive with --latest.
--latestImport the most recently modified native session.
--profile <id>Profile whose home holds the session. Needed only when several enabled profiles run this harness.
--bundle <id>Associate an existing configured repository bundle.
--title <text>Set the title displayed in the dashboard.
--allow-dirtyProceed when the source Git checkouts have uncommitted changes.
--allow-dirty-localCompatibility alias for --allow-dirty.
--allow-omitted-non-gitAcknowledge that modified non-Git or scratch directories will be omitted.
--review <off|push>Set the imported session’s review mode, overriding global reviewers.
--reviewer MODEL[:EFFORT]Add a reviewer choice; repeat up to three times. The final colon is an effort separator only for minimal, low, medium, high, xhigh, or max (for example, x:0:high uses model x:0 and effort high). A trailing colon means no effort and removes that colon, so foo:high: selects model foo:high. Other suffixes, such as :free or :latest, remain part of the model ID.
--workspace <name>Workspace to put the imported session in. Needed when the instance has more than one workspace.

Import leaves the source checkout and the harness’s source transcript unchanged. The imported session starts suspended and is available through prefix+g.

Examples:

Terminal window
mj import codex --latest --bundle product
mj import claude --session 018f2d00-0000-7000-8000-000000000000 \
--bundle product --title "Finish migration"
mj checkpoint --session <session-id>

Creates and verifies a recovery copy for an active session. It waits for a safe dispatch boundary, then lets normal work continue while the archive is packaged where supported. Mjolnir also checkpoints completed idle turns automatically, throttled to roughly one checkpoint per ten minutes.

mj move --session <id> [--target <target-id>] [--profile <profile-id>]
[--queue discard|start] [--clear-resources] [--yes] [--json]
[--prepare] [--allow-large-transfer] [--exclude <repository>:<path>]...
[--subagents native|mjolnir|none]
[--subagent-straightforward <model>] [--subagent-straightforward-effort <effort>]
[--subagent-subtle <model>] [--subagent-subtle-effort <effort>]
[--oracle enabled|disabled]
[--subagent-oracle <model>] [--subagent-oracle-effort <effort>]
mj move-sources --session <id> [--cleanup <operation-id> --yes]

Move keeps the logical session, workspace, transcript, and recoverable repository state while rebuilding its execution environment. At least one of --target or --profile is required; an omitted selector keeps its current value. The destination must satisfy the same compatibility checks as resume. Moving between harnesses creates a bounded transcript handoff rather than copying harness-private process state.

Move inherits the source resource sizing and attached directories by default. --clear-resources explicitly removes inherited sizing so the destination uses its configured defaults; attached directories remain part of the fixed workspace selection.

--subagents native switches to harness delegation; --subagents mjolnir uses the global task choices in [subagents.straightforward] and, when configured, [subagents.subtle], or provide a session override for either type. --subagent-straightforward M and --subagent-subtle M independently override the matching task choice for this session; each unspecified type uses its global choice. Either override implies Mjolnir. The matching --subagent-*-effort flag requires its model flag. Omitted policy retains the session’s override. --subagents none removes delegation tools entirely; it needs a Claude, Codex, DSH, or OpenCode session. A retry of a sealed Move retains its recorded policy; an explicit different policy is refused.

--oracle enabled|disabled turns the session’s oracle on or off, and --subagent-oracle M overrides the oracle model for this session and implies enabled; neither depends on the delegation policy. --subagent-oracle-effort requires --subagent-oracle, and adaptive is invalid. Omitted flags retain the session’s choice, and turning the oracle off keeps its model override. Combining --subagent-oracle with --oracle disabled is refused. When you ask for the oracle but no model is configured or given, the Move still runs with the oracle off and says so.

A session that works in a bare checkout on an SSH host cannot move to another machine. Its working tree is the checkout itself, so --target is refused with this session's working tree lives on <host>; resume it there. Such a session can change profile in place, or move to another bare target on the same host; that Move is the same in-place profile switch, keeping the checkout and transferring no files. To continue elsewhere, push its branch and start a new session from that branch. The same rule applies to mj resume --target.

When the move copies a workspace, it transfers the repository history and tracked edits, plus the untracked files you keep selected. Three flags control that selection:

  • --prepare inspects the transfer and prints the preparation as JSON without moving anything. Use it to see the eligible files, their sizes, and any blockers.
  • --exclude <repository>:<path> leaves an untracked file or directory at the source. Repeat it for each path.
  • --allow-large-transfer accepts a workspace transfer of at least 1 GB. Without it, a large selection is refused, and the error says which of these flags can resolve it.

Paths left behind stay in a retained source. mj move-sources --session <id> lists the retained sources for a session as JSON. To delete one together with its excluded files, pass its operation ID as --cleanup <operation-id> and confirm with --yes; --cleanup without --yes is refused.

An interactive invocation prepares the destination and asks for confirmation. --yes confirms the interruption for unattended use, but it does not choose what to do with queued work. If commands are pending, unattended use must pass --queue discard or --queue start; discard is the default only in an interactive confirmation. For start, commands are accepted in their original order after destination readiness. The interrupted active prompt is never replayed.

Human output reports the operation ID and phase progress. --json suppresses progress on stdout and emits one final object containing the operation ID, session ID, resolved profile and target, outcome, and any recovery guidance. Failures return a nonzero status. Ctrl-C requests cancellation from the daemon; disconnecting a client does not cancel a detached move.

mj upgrade-container --session <id>
mj upgrade-container --session <id> --acknowledge-writable-layer-loss
mj upgrade-container --session <id> --cancel

The first command previews the inspected current image, selected target and preservation warning. The acknowledgment queues a durable request; accepted work finishes before replacement starts. Workspace and build-cache mounts are retained. Writable-layer files, including manually installed packages, may be lost. A checkpoint does not preserve the whole environment. Pending requests survive daemon replacement and can be cancelled before replacement begins. Unsupported storage layouts fail before the worker is stopped. Normal Restart agent keeps the existing container and its filesystem.

mj recover scan [--json] [--all-instances]
mj recover adopt --session <id> --target <id> [--profile <id>] [--bundle <id>] [--all-instances]
mj recover destroy --session <id> --target <id> --confirm <id> [--all-instances]
  • scan lists Mjolnir-managed workers that exist on a target but are absent from controller state. By default it lists only workers this Mjolnir instance created (the --instance name, or a fingerprint of the data directory). --all-instances also lists workers created by other instances and workers from older builds that carry no instance stamp; the plain output shows which instance created each one.
  • adopt probes a worker and adds it back to state. --profile and --bundle are needed only for older current-v1 workers created before ownership markers were recorded. Adopting a worker from another or an unknown instance requires --all-instances.
  • destroy deletes an untracked managed resource. --confirm must repeat the exact session ID to make accidental deletion harder, and a worker from another or an unknown instance is refused without --all-instances.

Inspect scan output before adopting or destroying anything. See session recovery and durability for the surrounding guarantees.

mj workspaces list [--json]
mj workspaces create <name> [--json]
mj new (--workspace <name> | --workspace-id <id>) [--profile <id>] [--target <id>] [--bundle <id>]
[--project-directory <path>] [--at <sha> [--no-later-history]]
[--branch <name>] [--base <revision>]
[--title <text>]
[--model <name>] [--effort <name>] [--subagents native|mjolnir|none]
[--subagent-straightforward <model>] [--subagent-straightforward-effort <effort>]
[--subagent-subtle <model>] [--subagent-subtle-effort <effort>]
[--oracle enabled|disabled]
[--subagent-oracle <model>] [--subagent-oracle-effort <effort>]
[--review <off|push>] [--reviewer MODEL[:EFFORT]]...
[--no-project-memory] [--no-github-events] [--cpus <count>] [--memory-gib <GiB>]
[--prompt-file <path>] [<prompt>|-] [--json]
mj prompt --session <id> [<text>|-] [--prompt-file <path>] [--command-id <id>]
[--timeout <seconds>] [--return-on-input] [--json]
mj message --session <id> [<text>|-] [--prompt-file <path>] [--command-id <id>] [--json]
mj wait --session <id> [--turn <turn-id>] [--timeout <seconds>] [--return-on-input] [--json]
mj transcript --session <id> [--after-seq <seq>] [--limit <count>]
[--role <role>]... [--finished-only] [--json]
mj diff --session <id> [--base <revision>] [--json]
mj export --session <id> [--kind patch|branch|bundle] [--branch <name>]
[--out <path>] [--json]
mj fetch --session <id> --path <path> [--stdout | --ls] [--json]
mj sessions [--session <id>] [--all] [--workspace <name>] [--json]
mj suspend --session <id> [--acknowledge-unpublished-work] [--json]
mj destroy --session <id> [--delete-branch] [--json]
mj resume (--session <id> | --wiki <sessionwiki-id>)
[--profile <id>] [--target <id>] [--workspace-id <id>] [--workspace <name>]
[--queue start|discard] [--json]
mj interrupt-turn --session <id> [--json]
mj review start --session <id> [--json]
mj review status --session <id> [--json]
mj stop-task --session <id> (<task-id> | --all) [--json]
mj api-info [--json]
mj events [--session <id>] [--workspace-id <id>] [--workspace <name>] [--after-seq <seq>]
mj usage --session <id> [--after-seq <seq>] [--limit <count>] [--json]
mj put-file --session <id> --path <workspace-relative path> [--overwrite] [--json] <source>|-
mj elicitations --session <id> [--json]
mj respond --session <id> --elicitation <id> [<response JSON>|-] [--response-file <path>]
mj models --profile <id> [--model <name> | --unrestricted] [--json]
mj set-config --session <id> [--key <key> --value <value>] [--json]
mj ui pane (sessions | targets | profiles) (minimized | standard | maximized) [--json]
mj ui show <session-id> [--right | --below] [--json]
mj ui settings <path>=<value>... [--json]

mj new --at <sha> starts the workspace checked out at that full commit ID in the bundle’s primary repository, before the first prompt. It requires --bundle. With --at, --branch <name> creates that new branch at the commit; without --branch, HEAD is detached. Without --at, --branch <name> selects an existing branch to check out in a new isolated clone; otherwise the clone starts on the remote default branch. --base <revision> records the diff base for mj diff when it should not be --at; it defaults to --at and does not move the checkout. A local clone resolves the base in the source repository, while a network clone resolves it from fetched Git history. A base cannot be combined with a session that runs directly in the selected directory.

mj new --at <sha> --no-later-history gives the workspace the full history up to that commit and the --base commit, and nothing later. Use it when the agent must not see what happened after a commit, for example to review a pull request as it stood. See Start without later history for what the workspace holds and what the agent can still reach. --base must then be a full commit ID, the bundle must have one repository, and the session cannot be resumed or moved.

mj new --workspace eval --bundle product --target builder \
--at 0123456789abcdef0123456789abcdef01234567 --no-later-history \
--base 89abcdef0123456789abcdef0123456789abcdef --branch review/pr-670
  • mj new without --profile or --target uses the saved default for the missing one (the pair GET /api/v1/options reports as default). mj new never changes that default, the remembered sub-agent choice, or what the dashboard’s next new-session form preselects. Only interactive launches (the dashboard, mj go, mj simple, and the web or phone form) do.
  • mj new --model <name> chooses the enabled, usable profile offering that exact model with the strongest quota headroom relative to its reset times, then starts the session on it. It conflicts with --profile; the saved default profile breaks quota ties. If --effort is supplied, the selected profile must offer that exact effort for the model or the request fails.
  • mj new --subagents native|mjolnir|none selects delegation. Omitted defaults to Native, or to None for a DSH profile. DSH has no sub-agents of its own, so it refuses native and accepts mjolnir or none. none gives the session no delegation tools, native or Mjolnir; it needs a Claude, Codex, DSH, or OpenCode profile. Mjolnir requires a Claude, Codex, DSH, or OpenCode parent and a straightforward choice in [subagents.straightforward] or a session override. The subtle choice in [subagents.subtle] is optional. Configure global defaults in config.toml or TUI Settings → Sub-agents. --subagent-straightforward M and --subagent-subtle M override only their named task type; an unspecified type uses its global choice. Either implies --subagents mjolnir and cannot be combined with native or none. Each effort flag requires its matching model flag; omitted effort uses the harness default and adaptive is invalid. The retired --subagent-model and --subagent-effort flags fail with guidance to use the four per-type flags. single-model and all-models remain rejected. When both types are configured, each spawn selects straightforward or subtle. With only straightforward, the spawn tool has no task type, every child uses the straightforward choice, and the parent is told to delegate straightforward work. Mjolnir chooses an enabled profile offering its model and effort by quota.
  • mj new --oracle enabled|disabled chooses whether the session may consult the oracle, a read-only advisor behind the ask_oracle tool. It works with any delegation policy and any harness. Omitted uses the choice last made for the profile, which starts enabled. The oracle needs a model in [subagents.oracle] or a session override. --subagent-oracle M overrides the model for this session and implies enabled; --subagent-oracle-effort requires it, and adaptive is invalid. Combining --subagent-oracle with --oracle disabled is refused. When you ask for the oracle without a model, the session starts with the oracle off and says so.
  • mj new --review off|push sets the session’s review mode, overriding [review]. mj import <harness> takes the same option for the session it adopts. Repeat --reviewer MODEL[:EFFORT] up to three times to choose reviewers. See per-session settings. An empty global reviewer list means no reviewers; choose rows or press Autoconfigure in Settings → Code Review.
  • mj new --no-project-memory starts the session with no project memory: nothing from the workspace’s project memory is put in the agent’s startup context, and the harness is offered no memory directory to write to. Its sub-agents get none either. Use it when a session must see only its checkout and its prompt, as an evaluation run does. Without the flag the session loads the workspace’s project memory as before. See project memory.
  • mj new --no-github-events prevents the GitHub watcher from registering the session as a delivery target. Its sub-agents inherit the flag. This is the flag’s only effect. [github.watch] enabled and [jev] enabled control GitHub event production.
  • On a container target, mj new gives the session the same CPU and memory limit that the dashboard and the web viewer select: the size last chosen for that host, else 4 CPUs and 8 GiB. New containers need at least 2 CPUs and 4 GiB. CPU overcommit produces a warning on stderr and the launch proceeds. RAM overcommit also proceeds with a warning for Docker and Podman, including on macOS. Only Apple container targets refuse RAM overcommit, because their VMs do not return freed memory to macOS. Resume and move share this policy. --cpus <count> and --memory-gib <GiB> override either one. Other targets refuse both options.
  • --return-on-input makes mj prompt and mj wait return as soon as the agent asks for structured input, with the outcome input_required. Answer with mj elicitations and mj respond, then wait again.
  • By default, mj transcript omits tool and terminal entries. Repeat --role to select several kinds of entry from user, agent, thought, tool, terminal, plan, plan_proposal, and system. To include every kind, list all eight roles; there is no --all-roles option. --finished-only selects closed agent messages and can be combined only with --role agent; it implies the agent role when no role is given.
  • mj events prints durable session events as line-delimited JSON, following new events until you stop it. --after-seq replays from that sequence first.
  • mj usage reads recorded token usage for a session.
  • mj put-file uploads a file, or standard input with -, into an idle session’s workspace. It refuses to replace a file unless you pass --overwrite.
  • mj fetch copies a file, or a directory as a zip archive, from a session’s target to this machine and prints where it landed. See below.
  • mj models lists the models and efforts a profile offers, after the profile’s models allow-list; --unrestricted lists everything the harness advertises. Usage-priced profiles include known per-million-token rates; subscription and unknown billing modes are labeled without per-token prices. JSON includes a billing field and a price object or null for each model.
  • mj set-config applies user-settable session settings the harness advertises, such as the model or effort. The target permission policy owns permission settings; use plan mode to enter or leave Plan. Without --key and --value it lists the settings the session’s agent offers, with their choices.
  • mj ui changes what an open dashboard shows, as if you had pressed the keys, so an agent you ask to “maximize the Profiles pane” or “turn the bell off” can do it for you. pane sets one support pane’s size. show opens a session in the main conversation, or in a new pane to its right or below. settings opens Settings on the page of the first change with every change filled in but not saved: review it and press Save, or press Esc to discard it. Paths are the configuration file’s keys joined with dots, such as notify.bell=false, theme=dark or advanced.auto_afk_minutes=30. A value that reads as JSON is used as JSON, anything else as text, and null clears a field. The whole request is checked first: an unknown setting or value is refused with the reason, and nothing changes. With no dashboard open the command fails and says so. A request that arrives while a dialog is open waits until the dialog closes.

mj diff compares against the recorded launch base by default. Use --base to compare against an explicit Git commit or revision, for example after checking out an older task base. This does not change the session’s recorded baseline. --json returns diff, base, head, and head_descends_from_base; base and head are resolved commit IDs. The patch includes committed, staged, unstaged, and untracked work. An unknown base is refused. mj diff asks the worker for the metadata either way, so both forms need a worker that supports it; an older worker reports that the session needs upgrading.

When head_descends_from_base is false, the session’s HEAD is no longer a descendant of the base — the agent rebased, amended, or reset — so the diff carries history changes as well as session work. Without --json, mj diff writes the patch to standard output and one warning: line to standard error saying so. An older worker leaves the field out, and no warning is printed.

Every session lives in a workspace that the dashboard and the web viewer list, so mj new needs --workspace <name> (or --workspace-id <id>). Without it, mj new fails, lists the instance’s workspaces with their session counts, and says how to create one. A fresh instance has no workspace. Run mj workspaces create <name> first. It selects the workspace when the name already exists, so a script can run it every time:

Terminal window
mj workspaces create agents
mj new --workspace agents --project-directory . "Fix the failing test"

The terminal dashboard still names its first workspace after the directory it was started in. A store from an earlier release that holds sessions made without a workspace lists them in a workspace named default.

mj sessions lists top-level sessions by default. Add --all to include Mjolnir sub-agents.

These commands run one Mjolnir session as a subagent: mj new starts it with a first prompt and prints its id, mj wait blocks until the turn ends and prints the outcome, the elapsed time, the number of tool calls, and the agent’s final message, and mj prompt does both for the next prompt. A prompt comes from the positional argument, from --prompt-file, or from standard input when the argument is -; mj message takes its text the same way.

Each command has one behavior: mj prompt always waits, and mj message never does.

mj prompt --session <id> <text> sends the text as your prompt and waits for that turn to end. Before it waits, it prints turn N on standard error, so if the wait is cut off you can go on with mj wait --session <id> --turn N. With --json, standard output holds only the wait’s result. --command-id <id> makes the send safe to retry: after an ambiguous reply, run the same command with the same ID and you get the same turn, not a second prompt. The retired --wait flag is still accepted and changes nothing. Because a prompt sent this way reads exactly as your own, mj prompt refuses to run inside a session (when MJ_SESSION_ID is set); an agent sends with mj message and follows with mj wait.

mj message --session <id> <text> sends and returns. From your own shell (MJ_SESSION_ID not set), it submits the text as your prompt, just as mj prompt does: the session starts a turn with it, takes it into the running turn, or queues it. It prints turn N; follow it with mj wait --session <id> --turn N when you want the result. --command-id <id> makes it safe to retry in the same way. Inside a session (MJ_SESSION_ID set), mj message sends a message to another top-level session, or to one of the caller’s own sub-agents. Messages from sessions show the sender’s title and full ID, with a reply instruction, and --command-id identifies the message across retries.

The first line of a wait says how the turn ended in the same words as mj sessions --session <id>, for example turn finished (completed, end of turn) in 5.3s · 4 tool calls or turn error (failed: harness inactive) in 30.3s. The tool-call count appears when the turn made any. The line carries no turn number: the number mj prompt and mj message print is the handle mj wait --turn takes, not a count. With --json, the stop_reason field keeps the harness’s own spelling, such as EndTurn, and turn_id and tool_calls are separate fields.

mj wait answers one of two questions:

  • mj wait --session <id> --turn <turn-id> waits until that turn has ended and says how. The turn ID is the number mj new and mj message print. A server retry of the turn is part of it, but nothing that starts new work is: not a turn the agent starts on its own, background work it left running, or Mjolnir’s automatic continuation. When the session is still busy as the wait answers, the first line ends with what it is doing, in the dashboard’s words, for example turn finished (completed, end of turn) in 5.3s; the session is not idle: [BG 2m10s]. --json carries it as next.
  • mj wait --session <id> without --turn waits until the session settles: the agent is idle, nothing is queued, and the session can take a prompt. That includes turns the agent starts on its own, so it can keep waiting long after your prompt’s turn ended. To learn when your prompt’s turn ended, pass --turn, or use mj prompt.

mj resume continues a session that mj suspend suspended. The session keeps its id, its transcript, and its work; Mjolnir provisions a fresh target and restores the verified checkpoint. Every selector is optional: the session’s own record supplies the profile, the target, and the workspace, so mj resume --session <id> is the whole command for continuing where you left off. --profile and --target resume somewhere else, and --queue decides whether prompts queued when the session was suspended are started or discarded (start by default).

mj resume --wiki <sessionwiki-id> continues a session found in the SessionWiki index, whoever ran it, so an agent that searched for earlier work does not have to decide what kind of row it found. Mjolnir takes one of three branches and says which one it took. A Mjolnir session it still has a record of is resumed exactly as --session resumes it. A Mjolnir session whose record the archive job destroyed is restored: a new session starts with a compacted hand-off from the indexed transcript, using --profile and --target when given and the profile and target recorded in the index otherwise. Another tool’s session (Claude Code, Codex, Kimi Code, Grok Build, Muse) is imported from that harness’s own home and then resumed, which needs the session to still be there; the error names the indexed path so you can import it by hand if it is not. --wiki and --session are mutually exclusive, and --json answers with action (resume, restore or import), session_id and wiki_id.

mj sessions --session <id> accepts a SessionWiki id too. An id that names no Mjolnir session is looked up in the index and reported as mine, archived or native, with the tool, the indexed path and the Mjolnir session id when there is one, so an agent can explain what it found before it continues it.

The command answers as soon as the daemon has taken the session, because restoring an archive takes minutes. Follow it with mj wait --session <id>, which blocks while the resume runs and reports the reason if it fails, and then mj set-config, mj prompt and mj message as for any live session. A session that is already running is refused: suspend it first. mj move is the command for a live session that should continue elsewhere.

mj sessions --json and mj sessions --session <id> --json include background_tasks, with each task’s opaque id, command, started_at_ms, and can_stop. Use mj stop-task --session <id> <task-id> to stop one task, or --all to request stops for every currently listed stoppable task. Unsupported tasks are skipped and reported. With --json, the result contains session_id, accepted_task_ids, skipped_task_ids, and a failures object mapping task IDs to errors. All selected stops are attempted; any failure makes the command exit nonzero. Acceptance does not establish that the task has exited: inspect the session again to confirm it disappeared.

mj suspend saves a verified recovery copy and releases the environment. It reports acceptance; follow it with mj wait --session <id> to observe completion or a reported failure. A session’s active Mjolnir sub-agents are stopped without a recovery copy of their own, and only the session is suspended. When some of them have not handed back their reports, mj suspend prints a warning on standard error, such as warning: 2 sub-agents have not handed back; suspending stops them. With --json the answer carries stopped_subagents, subagents_not_handed_back, and warning (omitted when there is none). When the session resumes, its agent is told which sub-agents were stopped. For an independent clone whose work may be unpublished, pass --acknowledge-unpublished-work after reviewing the warning; suspension still verifies the recovery copy before releasing the clone. mj interrupt-turn interrupts only the current turn and keeps the session available for another prompt.

mj review start reviews the turn a session just finished with the session’s reviewer, as /review does in Prompt. It answers once the review has opened, which can take a few minutes while a reviewer is chosen and started, and fails with the reason when the session is still working, has queued prompts, is a sub-agent, or has no usable reviewer. mj review status prints the open review’s status, reviewer role, and verdict, or no review is open. The conversation prompt stays available while the review runs. See Code review.

mj destroy permanently removes the live session record, environment, and recovery archive. Its conversation is indexed in SessionWiki first: mj sessions --session <id> can find the archived conversation, and mj resume --wiki <id> starts a new session from it. That restores conversation context, not the deleted checkout or unexported files. New managed clones have no branch in the source repository. For older linked worktree sessions, the managed branch remains unless --delete-branch is specified. mj close was removed: use mj suspend to keep the session, or mj destroy to remove it (this replaces mj close --force). mj cancel-turn was removed too; use mj interrupt-turn. Both old names fail with a notice naming their replacement and perform no lifecycle action.

mj export writes a patch or a bundle to --out, or to standard output when no file is named; --kind branch pushes the session’s work and reports the branch and remote instead. With --json and --out, mj export prints one object with the path, the bytes written, and the format (patch or bundle); with --json and no --out the export itself is still the output.

mj fetch reads a path on a session’s target, which may be another host or a container, so one session can read evidence another session cites. A relative --path resolves in the directory the session’s agent runs in; an absolute one is read as the target spells it. After symlinks are resolved, the path must lie in the session’s workspace, its sub-agent report directories, or the target’s temporary directory. A file is copied as it is, up to 16 MiB. A directory arrives as a zip archive of at most 16 MiB, made from at most 128 MiB of files; symlinks inside it are stored as links, not followed. The copy lands in $TMPDIR/mjolnir/<session>/ followed by the path on the target, with .zip after a directory’s name, and mj fetch prints that local path. For example, mj fetch --session d8443f17… --path /tmp/rust-veto-cf prints /tmp/mjolnir/d8443f17…/tmp/rust-veto-cf.zip. With --json it prints the local path, the kind (file or directory), the source path on the target, and the bytes written. --stdout writes the file, or the zip archive, to standard output instead.

mj fetch --ls lists the path on standard output instead of copying it: a directory’s entries in name order, or the file itself. Each line gives the size in bytes (- for a directory), the modification time in UTC, and the name, with / after a directory and -> target after a symlink. Symlinks are listed, not followed. With --json it prints the resolved path and an entries array; each entry has name, kind (file, directory, symlink, or other), size, modified (RFC 3339, or null when the target records none), and target for a symlink. mj transcript pages by --after-seq; use the response’s next_after_seq for the next page. The selected roles are filtered before the page limit. The cursor advances across entries outside that selection, so a page with no matching items can still make progress. latest_seq is the newest sequence in the full transcript. --limit sets a target number of matching items per page; tied entries with the same sequence stay together, so a page can exceed the target.

With --finished-only, next_after_seq is an exclusive resume cursor for the finished agent prefix: it never passes the sequence of the earliest currently open agent message. If the page reaches that open message, the cursor stops at the preceding sequence; poll again with that exact cursor, and the message is returned after its body reports streaming: false. If the page limit is reached first, the cursor is the last returned sequence. With no open agent message remaining, it can advance across non-agent rows to latest_seq, even when the page has no items. Here, “finished” means the stored stream was closed at the time of the read. ACP can still append late chunks to the same stable_id; a consumer that deduplicates only by stable_id may ignore that later revision.

Each command in this section except mj events and mj respond takes --json to print its JSON result. For mj transcript, the server applies the default role selection or the repeated --role selection before paging, and returns next_after_seq and latest_seq with the response. --finished-only filters agent messages and uses the finished-prefix cursor described above. mj events always prints JSON lines. mj checkpoint, mj login, mj app, mj go, mj setup, and the mj daemon commands do not take --json. The commands in this section are clients for the HTTP API, which documents the routes, the wait outcomes, the export preconditions, and the bearer token these commands read. mj api-info prints the base URL and the token file.

Most behavior belongs in configuration. These environment variables select filesystem locations or companion binaries before configuration loads:

VariablePurpose
MJ_CONFIG_DIRDirectory containing config.toml; overrides the platform config directory.
MJ_DATA_DIRRoot for Mjolnir’s database, logs, archives, and other local state.
MJ_WORKER_BINARYExplicit target-compatible worker binary.
MJ_WORKER_DIRDirectory searched for bundled worker binaries.
MJ_WORKER_URLRemote worker URL fallback; it may contain {target} and requires MJ_WORKER_SHA256.
MJ_WORKER_SHA256Required 64-character hexadecimal digest when MJ_WORKER_URL is set.
MJ_DESKTOP_BINARYExplicit mj-desktop executable used by mj app.
MJ_CONTROLLER_BINARYExplicit controller executable used by companion launchers.
MJ_VOICE_WORKERExplicit local voice-worker executable.
CODEX_HOMECodex home used by setup discovery and native import.
CLAUDE_CONFIG_DIRClaude Code home used by setup discovery and native import.
KIMI_CODE_HOMEKimi Code home used by setup discovery and native import.
GROK_HOMEGrok Build home used by setup discovery and native import.
DSH_ACP_DATA_DIRDSH home used by setup discovery.
DEEPSEEK_API_KEYDSH’s API key, passed to DSH sessions when DSH’s own credentials hold none. It outranks the profile’s environment.
XDG_CONFIG_HOME, XDG_DATA_HOMEMuse configuration and external native session storage roots, respectively.
GH_TOKEN / GITHUB_TOKENGitHub token available for syncing into every live target except a bare runtime on this machine.
SLOPCOP_TOKEN / SLOPCOP_SERVICEslopcop login synced into every live target except a bare runtime on this machine. They outrank the login slopcop login stored, as they do for slopcop itself.
GIT_SSH_COMMANDSSH command used by checkpoint/archive Git operations.
RUST_LOGController logging filter.

Setting MJ_WORKER_URL requires MJ_WORKER_SHA256; an unverified downloaded worker is not accepted. A digest set without a URL is ignored.

Run mj <command> --help for Clap’s installed-version spelling and option summary. Continue to troubleshooting for logs, target diagnosis, and common command failures.