CLI Reference
The mikro CLI is the main tool for building, deploying, and managing Mikro.js projects.
mikro dev
Start development mode with live reload. Builds your TypeScript, deploys it to the device over serial, and watches for changes. Every time you save a file, the new code is sent to the device within seconds.
mikro dev [ENTRY]| Option | Description |
|---|---|
ENTRY | Entry file (default: main field in package.json) |
-p, --port PORT | Serial port (auto-detected if omitted) |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.development |
--force-deploy | Force full deploy, ignoring cached checksums |
--no-minify | Skip minification |
--no-bytecode | Skip bytecode compilation |
--loglevel LEVEL | Log level: none, error, warn, info, debug. Default: debug |
--json | Output as JSON |
See Build options for details on --no-minify, --loglevel, and other build flags.
If the app declares a config schema, each deploy round ships the manifest the config defaults live in, so ota.config() works without a registry. Per-device values beyond the defaults come from a registry check-in.
To run on the host simulator instead of a device, use mikro sim dev.
mikro deploy
One-shot deploy to a connected device. This is treated as a production deployment: MIKRO_ENV is set to "production" and --loglevel defaults to warn (stripping console.debug, console.log, and console.info calls from the build).
The build is staged over the cable and installed at the next boot, before the app loads, so a deploy succeeds even when the running app has fragmented the device's heap. The deploy restarts the device, waits for it to come back, and reports the install outcome; on failure the previous app keeps running.
If the app declares a config schema, the deployed build carries the schema defaults in its manifest, and ota.config() reads them until a registry delivers a document. The deploy also drops config-pairing state left over from a previous OTA install cycle (a staged or rollback document, a running trial, the last failure report); the delivered document itself is kept, and its version stamp decides whether the new build still reads it.
mikro deploy [ENTRY]| Option | Description |
|---|---|
ENTRY | Entry file (default: main field in package.json) |
-p, --port PORT | Serial port (auto-detected if omitted) |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.production |
--console | Attach console after deploy and restart device |
--recover | Reset into safe mode before deploying. See Troubleshooting |
--no-restart | Stage the build without restarting; it installs at the next device boot |
--no-minify | Skip minification |
--loglevel LEVEL | Log level: none, error, warn, info, debug. Default: warn |
--json | Output as JSON |
See Build options for details on --no-minify, --loglevel, and other build flags.
mikro build
Build your app without deploying. Produces a build directory with bundled and optionally bytecode-compiled output.
mikro build [ENTRY] [-o DIR]| Option | Description |
|---|---|
ENTRY | Entry file (default: main field in package.json) |
-o, --out-dir DIR | Output directory (default: .mikro/build in the project root) |
--no-minify | Skip minification |
--no-bytecode | Skip bytecode compilation |
--loglevel LEVEL | Log level: none, error, warn, info, debug. Default: warn |
--json | Output as JSON |
The output directory is deleted and recreated on every build. The default is inside the hidden .mikro/ directory; to write somewhere you can browse, pass -o, for example mikro build -o out.
A directory passed with -o gets a .mikro-build marker file. The build deletes an existing directory only if it is empty or has this marker, so mikro build -o . or -o src fails instead of deleting your files.
See Build options for details on --no-minify, --loglevel, and other build flags.
mikro flash
Flash the Mikro.js runtime firmware to a device. You only need to do this once per board, or when updating Mikro.js.
mikro flash| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
--chip CHIP | The device's chip (for example esp32c6). Detected from the connected device if omitted |
--board BOARD | Board name, for example @acme/devboard or esp32c6-generic. Without it: board in mikro.config.ts, else the board in the project's dependencies (the one for the device's chip when there are several), else the board a device on the generic firmware was flashed as, else the generic board for the chip |
--features FEATURES | Flash the leanest of the board's images with these features, comma-separated (wifi, or wifi,ble), no-<feature> for the full image without that feature (no-ble, or no-ble+no-wifi as in the image names), min for the leanest image, or full for the full image. Without it, a reflash keeps the image the device runs, except with --force, which flashes the full image |
--build-dir DIR | Path to a local ESP-IDF build directory |
--from URL | The URL of a firmware archive (a .tar.gz, as mikro fw pack writes it), flashed as it is |
--baud BAUD | Baud rate for flashing (default: 460800) |
-y, --yes | Skip confirmation prompt |
--force | Flash even if the device reports custom firmware, or the new partition table would shrink the app filesystem and erase its files. It doesn't read the device first, so it keeps neither the image the device runs nor the board a device on the generic firmware was flashed as |
The generic boards also have leaner images: no-ble without Bluetooth, and no-ble+no-wifi without either radio. An app that uses mikro/wifi but not mikro/ble gets more memory with mikro flash --features wifi, which flashes no-ble: the leanest image with WiFi. An app that uses neither gets no-ble+no-wifi with mikro flash --features min, the leanest image. The ESP32 also has no-wifi, for an app that uses BLE but not WiFi: mikro flash --features ble flashes it. When an app needs a feature the device's image lacks, mikro dev, mikro deploy and mikro test stop and print the --features to flash with.
On a module with more flash than the firmware was built for, the generic firmware, a board package's image and --from firmware give the app filesystem the rest of the flash, up to 16 MB. This applies when user is the last partition, as in the default partition table.
A reflash of the generic firmware or a board package's image writes only what changed. It skips the bootloader and partition table when the device already has them. When the device runs the same image at the same version, it writes only the 4 KB sectors of the app that differ: a new board name changes two. The flash is checked first each time, so a device that holds something else gets the whole file. --force doesn't read the device and writes the whole app, and --build-dir and --from write every file.
TIP
--build-dir and --from are mutually exclusive. Use --build-dir for firmware you built from source, and --from for an archive someone shared. To flash the firmware of another Mikro.js version, install that version of mikro and run pn mikro flash.
mikro idf
Run ESP-IDF's idf.py to build and flash custom firmware. The arguments go to idf.py unchanged:
pn mikro idf set-target esp32c6
pn mikro idf build flash monitorWhen idf.py is not on PATH, the command runs it through EIM with eim run, which activates ESP-IDF first. It exits with idf.py's exit code.
In a firmware project (a folder with a CMakeLists.txt), it also tells CMake where the project's @mikrojs/firmware is: it resolves the package from the project and passes -DMikroFirmware_DIR to idf.py, for the project's find_package(MikroFirmware ...). A firmware project does not build with plain idf.py, so use mikro idf for every step, including flash and monitor.
mikro fw
mikro fw pack
Build a custom firmware project and pack it into an archive that mikro flash --from can flash: flasher_args.json, the files it lists, and firmware.json. Run it in the project folder. In a board package, it runs mikro fw build first and packs each board's image. A board that runs the generic firmware gets its firmware.json and no archive: its image ships with mikro.
pn mikro fw pack| Option | Description |
|---|---|
--out FILE | Output path for the archive (default: ./mikro-fw-<name>-<chip>.tar.gz, without the chip when the name already ends with it or is <chip>-generic, or ./mikro-fw-<chip>.tar.gz for firmware without a name) |
--board BOARD | In a board package, pack only this board: its key in boards.config.ts (./t-display, or t-display) or its name. A bare --board asks which, or without a terminal lists the boards |
--parallel N | In a board package, build up to N images at once, as mikro fw build --parallel does |
mikro fw build
Build the boards in a board package's boards.config.ts and write each image into the folder that the board's firmware export points at, then check the package as mikro fw check does. Run it in the package. Each board builds from a firmware project generated in .mikro/fw-<board>, into .mikro/build-fw-<board> (.mikro/fw and .mikro/build-fw for the board at .). A board package runs it from npm's prepack script, so npm pack and npm publish always include fresh images. Each board's new images replace its image folder once they are all built, so a failed build leaves the last ones. Before it builds, it stops if the folder holds anything but that board's images, so a dist set to the wrong folder loses nothing. A board that runs the generic firmware needs no ESP-IDF: mikro fw build writes its firmware.json, which names the generic board.
pn mikro fw build| Option | Description |
|---|---|
--board BOARD | Build only this board: its key in boards.config.ts (./t-display, or t-display) or its name. A bare --board asks which, or without a terminal lists the boards |
--image IMAGE | Build only this image of each board, full or one of its leaner images, and keep the others. A bare --image asks which, or without a terminal lists the images. A board that runs the generic firmware has no images of its own, so --image refuses it |
--parallel N | Build up to N images at once, from all the boards, instead of one after another (--parallel 4). Each build writes its output to a log beside its build folder (.mikro/build-fw+no-ble.log), and a line per image says how it went. After a failure, no more builds start |
--flash | Then flash the image it built, as mikro flash does. It needs one board (--board in a package with several) and, for a board with leaner images, --image |
mikro fw list
List the boards in a board package's boards.config.ts and their images, one line per board. A board that runs the generic firmware shows the generic board instead (runs esp32-generic). With --json, it prints them as JSON, for example to build each image in a CI job of its own with mikro fw build --board <board> --image <image>. There a generic board has firmware and no images, so a matrix built from images has no job for it; a plain mikro fw build, such as the one in prepack, writes its firmware.json.
pn mikro fw listmikro fw check
Check a board package's boards.config.ts, its firmware exports and their images: that the exports match the config, and that each image is built, complete, named correctly, from a Mikro.js version the CLI accepts, not older than its last build, and included in files. It exits with an error if anything is wrong.
pn mikro fw checkmikro ls
List connected devices. Each line shows the device's name, its serial port path, and details:
mikro lsswift-otter /dev/tty.usbmodem1101 (chip=esp32c6, id=2m68224yym)The name is the name the device reports for itself if one is set, otherwise its stable device ID. id is that same device ID as the firmware reports it; chip appears once you've connected to the device at least once.
| Option | Description |
|---|---|
--json | Output as JSON (includes name, deviceName, deviceId, serialNumber, chip, and USB metadata) |
mikro name
Give a device a memorable name. The device stores it itself, so it travels with the board: plug it into another machine and it still answers to the same name, and a registry the device is enrolled with shows the same one.
mikro name # show the connected device's name
mikro name set [NAME] # name it (omit NAME to use an `adjective-animal` suggestion, like `swift-otter`)
mikro name unset # clear the name| Option | Description |
|---|---|
-p, --port PORT | Device to act on (path, serial, or name). Defaults to the only connected device. |
--json | Output as JSON |
Because the name is written to the device, these need it connected. You can also run /name set <name> / /name unset inside mikro console or mikro dev.
A device with no name set shows its device ID instead. The suggestion offered by mikro name set is only ever used to create a name you then keep — nothing displays it until it is stored, so upgrading the CLI can never rename a board behind your back.
Devices enrolled with an update registry are always named: mikro ota enroll writes one if you don't pass --name, and thereafter a rename on either side syncs to the other at the next check-in.
Anywhere a command takes --port, the value can be a device path, its name, serial number, or device ID.
mikro console
Connect to the device's serial console. Shows runtime output and provides a REPL for interactive evaluation.
mikro console| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
--raw | Plain serial passthrough (no protocol framing) |
--hex | Show raw hex bytes |
--recover | Reset into safe mode before connecting (inspect a crash-looping device). See Troubleshooting |
mikro logs
Read or follow device logs. Three subcommands:
mikro logs tail— live stream from the wire (nologFilerequired).mikro logs pull— read the on-device file written by the file logger (requireslogFileinmikro.config.ts).mikro logs reset— clear the on-device log files (requireslogFileinmikro.config.ts).
mikro logs tail
Stream console output from the device without an interactive REPL. Useful for monitoring a running app or piping output to other tools.
mikro logs tail| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
-r, --restart | Restart the device first |
--loglevel | Drop device output below this level: none, error, warn, info, debug (default debug) |
mikro logs pull
Pull the on-device log file written by the file logger. With no destination, the log is streamed to stdout — older rotated content first, then the current generation, so the output is chronological. With a destination directory, both log.txt and log.txt.1 (if present) are written there as separate files.
# Stream to stdout
mikro logs pull
# Pipe through tools
mikro logs pull | grep ERROR
# Archive both generations
mikro logs pull ./forensics| Option | Description |
|---|---|
DEST | Optional directory to write files into |
-p, --port PORT | Serial port (auto-detected if omitted) |
The file logger flushes and releases its handle while the file is streamed off, so the pull sees a consistent snapshot. Any log lines emitted during the transfer are dropped; in practice this is a sub-second window.
mikro logs reset
Clear the on-device log files. The device deletes both log.txt and log.txt.1 and reopens a fresh, empty log. The running app is not interrupted and the device is not restarted.
mikro logs reset| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
mikro test
Run on-device tests. Discovers *.test.ts files, deploys them, and reports structured results. Each file runs in a fresh runtime with a full heap, so tests are isolated from each other.
mikro test [PATTERN]| Option | Description |
|---|---|
PATTERN | Glob pattern to filter test files (default: **/*.test.ts) |
-p, --port PORT | Serial port (auto-detected if omitted) |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.test |
--no-minify | Skip minification |
--no-bytecode | Skip bytecode compilation |
-t, --timeout MS | Per-file timeout in ms (default: 60000) |
-u, --update-heap | Overwrite committed heap snapshots with this run's measurements, boot figures included |
--heap-tolerance SIZE | Heap drift below which a snapshot is neither flagged nor rewritten |
--diagnostics | Show per-test heap progress and supervisor announcements |
--isolate | Restart the device before every file so each starts from a fresh boot; slower, but run order cannot affect results or heap figures |
-y, --yes | Skip confirmation prompt |
--json | Output as JSON |
See Build options for details on --no-minify and other build flags.
# Run all tests
mikro test
# Run a specific test file
mikro test 'test/smoke.test.ts'
# Run with env vars
mikro test --env-file .envTest files import from mikro/test:
import {describe, test, assert} from 'mikro/test'
describe('my feature', () => {
test('works', () => {
assert.equal(1 + 1, 2)
})
})The environment variable MIKRO_ENV is automatically set to "test" during test runs.
TIP
mikro test overwrites whatever app is currently on the device with the test manifest. The confirmation prompt exists so you don't do that by accident. Pass -y in CI or scripted runs when you know what you're doing.
To run tests in the host simulator, use mikro sim test.
mikro profile
Report the memory a device leaves for an app to consume. This is the device counterpart to the QuickJS baseline line in mikro sim profile: what is available before any of your code costs anything.
mikro profile esp32c6 js 218KB system 252.4KBThere are two ceilings, and either one can be the first you hit:
- js: the JS budget left before
mem_limitthrowsInternalError: out of memory. This is the figure the firmware boot banner prints. - system: the free chip heap that native allocations draw on (TLS records, WiFi buffers, drivers).
QuickJS allocates out of the system heap, so JS growth is capped by whichever is smaller. Both are stored as boot in __heap_snapshots__/<chip>.json, the same file mikro test writes per-test figures to. A test run reads the same handshake and records them, so the usual way to keep the figures current is to run the suite. This command reports them on demand and writes only with --write.
They track the firmware and the project's runtime configuration rather than app code, so they move when sdkconfig, native modules, or the ESP-IDF version move. They also move when memReserved changes, since mem_limit is derived as free heap minus that reserve. memReserved moves js one-for-one and leaves system untouched, which is what tells a reserve change apart from a real firmware regression.
The device reports both in the ready handshake, captured at boot before it evaluated your app, so a normal run only connects and reads. There is one exception: when the device booted with a different memReserved than the project config, the reading would describe the old reserve, so the command offers to deploy the project first. Accepting replaces the app on the device with a production build.
| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
--write | Record this run's reading as the committed boot snapshot |
--heap-tolerance SIZE | Drift below which the snapshot is neither flagged nor rewritten |
--json | Output as JSON |
When a reading shows less free memory than the stored figure, the command reports a regression and exits non-zero; more free memory is reported as an improvement worth recording. A move in either ceiling counts, and --write records both.
TIP
Firmware predating this handshake field does not report the figures, and the command says so. Rebuild and flash to get them.
mikro env
Manage environment variables stored on the device. Variables persist across reboots and are accessible via mikro/env or import.meta.env in your code.
mikro env list
List all environment variables on the device.
mikro env list| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
--json | Output as JSON |
mikro env set
Set an environment variable on the device.
mikro env set KEY [VALUE]| Option | Description |
|---|---|
KEY | Variable name (max 15 characters) |
VALUE | Variable value. Allowed only with --no-secret. Without it, omit VALUE and the CLI prompts you for it |
--no-secret | Pass VALUE as an argument and store it as non-secret (visible in env list). Requires a VALUE argument |
-p, --port PORT | Serial port (auto-detected if omitted) |
Secrets are entered via a hidden prompt so they don't leak into shell history or ps output. Plain values can be passed inline with --no-secret.
# Secret: prompts for value (hidden input)
mikro env set API_KEY
# Non-secret: pass VALUE inline
mikro env set WIFI_SSID MyNetwork --no-secretYou can also mark .env entries non-secret with a # @no-secret comment line above them.
mikro env delete
Delete an environment variable from the device.
mikro env delete KEY| Option | Description |
|---|---|
KEY | Variable name to delete |
-p, --port PORT | Serial port (auto-detected if omitted) |
mikro ota
Pack, push, and release app builds for over-the-air updates, and enroll devices with an update registry. See the OTA guide.
Which registry to use lives in .mikro/registry.json (project) or ~/.mikro/registry.json (user), as {"url": …, "token": …}; push, release, and enroll read it so none need flags once it is set. Precedence: flags, then MIKRO_OTA_TOKEN, then the project file, then the user file.
mikro ota setup
Configure which update registry to use and write it to .mikro/registry.json. Prompts for the registry url (checked for reachability), then obtains the token: registries that support browser login print a one-time code and offer to open the approval page (press Enter, or open the url yourself), then wait for the exchange. The CLI sends your project's app name so the registry can offer a token scoped to it. Other registries fall back to a hidden token prompt. Re-run it any time the registry moves or the token rotates; existing values are offered as defaults.
The browser is only opened from an interactive local terminal, since someone has to press Enter. Over ssh, in agent mode, or with stdin redirected, setup prints the url and the code and waits for you to approve from wherever your browser is.
mikro ota setup| Option | Description |
|---|---|
--registry URL | Registry base URL (skips the url prompt) |
--token TOKEN | Registry token (with --registry, setup runs without prompts) |
--user | Write ~/.mikro/registry.json (all projects) instead of the project's |
--force | Accept a url that does not identify itself as a Mikro.js registry |
mikro ota pack
Build the current project to bytecode and pack it into a deployable OTA build: a .tgz with a manifest recording the app name, the firmware version it requires, and the bytecode version it targets. An app that declares a config schema gets it recorded there too, alongside the defaults it materializes: the registry validates config against the schema, and the device reads the defaults.
mikro ota pack| Option | Description |
|---|---|
--out FILE | Output path for the build (default: ./app-<version>.tgz) |
--snapshot | Derive a unique version so iteration needs no version bump (see below) |
--no-minify | Skip minification |
--loglevel LEVEL | Log level: none, error, warn, info, debug |
See Build options for details on --no-minify, --minifier, and other build flags.
mikro ota push
Build, pack, and upload a build to your OTA registry. Without --tarball, the current project is built and packed first (same as mikro ota pack). By default the build is uploaded but not served to any device; pass --release CHANNEL to also point a channel at it (see Release channels).
mikro ota push| Option | Description |
|---|---|
--registry URL | Registry origin (default: .mikro/registry.json) |
--tarball FILE | Push a pre-packed .tgz instead of building |
--release CHANNEL | Also release the build to this channel (default: not served) |
--token TOKEN | Registry auth token (default: MIKRO_OTA_TOKEN env var) |
--note TEXT | Free-text note stored with the build (for example what changed) |
--create | Create the app on first push instead of failing on an unknown app |
--snapshot | Derive a unique version so iteration needs no version bump (see below) |
See Build options for details on --no-minify, --loglevel, and other build flags.
A registry stores each (app, version, firmware range) build once, so re-pushing changed bytes without bumping package.json fails (re-pushing the same bytes is an idempotent success, so a CI retry is safe). --snapshot sidesteps that during development by appending the UTC build time to the version as a semver prerelease (1.2.3-snapshot.20260723T003205Z). Snapshot versions therefore sort in build order under semver, so listed or sorted versions read chronologically; which build a device installs is decided by checksum, not by version order. Every push creates a new version, including a re-push of the same commit; which commit a build came from is recorded separately, in the build's source fields. With --tarball, pack with --snapshot instead; the version is fixed once a build is packed.
mikro ota release
Point a channel at a build that is already on the registry. Uploading (push) and releasing are separate steps: release moves the channel pointer only, it uploads nothing. Use it to graduate a proven build to a wider channel (mikro ota release 1.2.3 main) or to roll a channel back to an earlier version. See Release channels.
mikro ota release <version> <channel>| Option | Description |
|---|---|
VERSION | Version of an already-published build to serve |
CHANNEL | Channel to point at that build (for example beta or main) |
--registry URL | Registry origin (default: .mikro/registry.json) |
--token TOKEN | Registry auth token (default: MIKRO_OTA_TOKEN env var) |
mikro ota enroll
Enroll the connected device with an update registry: reads the device's hardware id, registers it, and writes the registry url and the returned update key to the device's system store, where deploys never touch them. See Enrolling devices.
mikro ota enroll| Option | Description |
|---|---|
--registry URL | Registry origin (default: .mikro/registry.json) |
--token TOKEN | Registry API token (default: MIKRO_OTA_TOKEN env var) |
--name NAME | Display name stored with the device in the registry |
--channel CHANNEL | Release channel to enroll the device on (default: main) |
--re-enroll | Rotate the update key when the device is already enrolled (the old one stops working) |
--update-key SECRET | Write an externally issued update key to the device; the registry is not contacted |
-p, --port PORT | Serial port (auto-detected if omitted) |
--json | Output as JSON |
mikro clean
Remove the deployed app from the device. The device restarts and boots into the REPL with no app.
mikro clean| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
--full | Remove all files and environment variables, not just the deployed app |
--recover | Reset into safe mode before cleaning. See Troubleshooting |
-y, --yes | Skip confirmation prompt |
To clean simulator state, use mikro sim clean or mikro sim reset.
With --full, all user-created files and environment variables are also removed. This requires confirmation (skip with -y).
mikro erase
Erase all flash on the device, performing a full factory reset. This removes firmware, application code, and all stored data.
mikro erase| Option | Description |
|---|---|
-p, --port PORT | Serial port (auto-detected if omitted) |
--baud BAUD | Baud rate (default: 460800) |
-y, --yes | Skip confirmation prompt |
DANGER
This erases everything on the device. You will need to re-flash firmware and re-deploy your app afterwards.
mikro sim
Run your code in the host simulator instead of on a real device. The simulator boots the same QuickJS runtime as a board would, talks the same protocol, and persists files and environment variables under .mikro/sim-fs/ and .mikro/nvs.json.
Only one sim process can run at a time per project. Long-lived commands (sim dev, sim repl) kill any predecessor on start. One-shot commands (sim deploy, sim test, sim env, sim profile, sim clean, sim reset) refuse to run while a sim is already alive.
Hardware builtins are stubbed; see Host simulator for details. Override the stubs by writing your own sim/<builtin>.stub.ts files (use mikro sim scaffold to generate starting points). Simulator memory and filesystem limits are configured in mikro.config.ts.
mikro sim dev
Watch + build + deploy + REPL against the simulator. The sim equivalent of mikro dev.
mikro sim dev [ENTRY]| Option | Description |
|---|---|
ENTRY | Entry file (default: main field in package.json) |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.simulator |
--no-minify | Skip minification |
--no-bytecode | Skip bytecode compilation |
mikro sim deploy
One-shot build + deploy to the simulator (no watch). The sim equivalent of mikro deploy.
mikro sim deploy [ENTRY]| Option | Description |
|---|---|
ENTRY | Entry file (default: main field in package.json) |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.simulator |
-e, --erase | Erase current app before uploading |
--no-restart | Do not restart sim after deploy |
--no-minify | Skip minification |
--no-bytecode | Skip bytecode compilation |
--json | Output as JSON |
mikro sim repl
Open an interactive REPL session on a fresh sim process. The sim equivalent of mikro console.
mikro sim replmikro sim test
Discover and run *.test.ts files in the simulator. The sim equivalent of mikro test.
mikro sim test [PATTERN]| Option | Description |
|---|---|
PATTERN | Glob pattern to filter test files (default: **/*.test.ts) |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.simulator |
--no-minify | Skip minification |
--no-bytecode | Skip bytecode compilation |
-t, --timeout MS | Per-file timeout in ms (default: 60000) |
-u, --update-heap | Overwrite committed heap snapshots with this run's measurements |
--heap-tolerance SIZE | Heap drift below which a snapshot is neither flagged nor rewritten |
--diagnostics | Show per-test heap progress and supervisor announcements |
--json | Output as JSON |
mikro sim env
Manage simulator environment variables. The sim equivalent of mikro env.
mikro sim env list
mikro sim env get KEY
mikro sim env set KEY [VALUE] [--no-secret]
mikro sim env delete KEYmikro sim clean
Remove the deployed app from the simulator (sim-fs/app/), leaving environment variables intact.
mikro sim cleanmikro sim reset
Erase the entire simulator state: filesystem and environment variables.
mikro sim reset [-y]| Option | Description |
|---|---|
-y, --yes | Skip confirmation prompt |
mikro sim profile
Run your app in the simulator and report per-module QuickJS heap usage, so you can see which imports are fat and which are cheap before deploying. Useful for debugging InternalError: out of memory failures where the cold-start module load is the suspect.
mikro sim profile [ENTRY]| Option | Description |
|---|---|
ENTRY | Entry file (default: main field in package.json) |
--mem-limit BYTES | QuickJS heap ceiling for the profile run (default: 32M) |
--memory-budget KB | Highlight rows against an explicit budget in KB |
--chip NAME | Preset budget for a chip (for example esp32c6 or esp32s3) |
--top N | Show only the N largest modules |
--sort KEY | Sort by size (default) or order (load order) |
--min-bytes N | Hide modules smaller than N bytes |
--include-native | Include native:* runtime modules (excluded by default) |
--include-builtins | Include mikro/* built-in modules (excluded by default) |
--only-native | Show only native:* modules |
--only-builtins | Show only mikro/* built-ins |
--json | Output as JSON |
--env-file FILE | Extra .env file, applied last (highest priority); see precedence |
--no-auto-env | Skip auto-loading of .env and .env.development |
By default, native:* runtime modules and mikro/* built-ins are hidden so you see only your own code's heap cost. Use --include-native / --include-builtins to add them back, or --only-native / --only-builtins for a focused view of just those categories.
The --mem-limit is intentionally high (default 32M) so the measurement run doesn't OOM. The profile output shows whether the app fits in the actual device budget.
mikro sim scaffold
Generate starting sim/<builtin>.stub.ts files in the project's sim/ directory. Use --overwrite to replace existing stubs.
mikro sim scaffold [--overwrite]Build options
Commands that build your code (dev, deploy, build, test, ota pack, ota push, and the sim equivalents) share these options:
--no-minifydisables minification. Useful for debugging; the output is larger but more readable in stack traces.--minifier MINIFIERselects the minifier:esbuild(default),terser, orswc. Can also be set viabuild.minifier.--minify-level LEVELsets minification aggressiveness:defaultormax. Can also be set viabuild.minifyLevel.--no-bytecodeskips compiling JavaScript to QuickJS bytecode. The device will parse JavaScript source at runtime, which uses more memory and is slower to start.--loglevel LEVELsets the build-time log level. Console calls below the threshold are eliminated as dead code by the minifier. Levels from most to least verbose:debug>info>warn>error>none.deployandbuilddefault towarnanddevtodebug, unlessbuild.logLevelis set.
Shell completion
mikro ships completion scripts for bash, zsh, and fish. Once installed, pressing Tab completes subcommands, flags, file paths, and, for -p / --port, the serial devices currently plugged into your machine.
Generate and install the script for your shell:
mkdir -p ~/.bash_completion.d
mikro completion bash > ~/.bash_completion.d/mikro
echo 'source ~/.bash_completion.d/mikro' >> ~/.bashrcmikro completion zsh > "${fpath[1]}/_mikro"
# reload completions in the current shell:
autoload -U compinit && compinitmikro completion fish > ~/.config/fish/completions/mikro.fishThen start a fresh shell session and try:
mikro <TAB> # → dev, deploy, env, build, flash, ...
mikro dev --<TAB> # → --port, --env-file, --no-minify, ...
mikro dev --port <TAB> # → live serial devices on your machineCompletion is also available as the --completion <shell> option, useful for piping into a one-off file without invoking the subcommand form.
Project-local installs
Shell completion only fires when the shell parses mikro as the first token of the command line. So it works for global installs and direnv-style setups that put node_modules/.bin on $PATH, but not for pnpm mikro, npx mikro, or npm run wrappers.
If you prefer a local install, a small shell function makes completion work by routing the bare mikro name through PATH, with the project's node_modules/.bin prepended:
mikro() {
PATH="./node_modules/.bin:$PATH" command mikro "$@"
}Add it to your .bashrc / .zshrc. The local binary wins when present; otherwise it falls back to a global install. Note that prepending a relative path has the same trust implication as direnv's PATH_add: a malicious project could plant its own node_modules/.bin/mikro.