Skip to content

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.

sh
mikro dev [ENTRY]
OptionDescription
ENTRYEntry file (default: main field in package.json)
-p, --port PORTSerial port (auto-detected if omitted)
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip auto-loading of .env and .env.development
--force-deployForce full deploy, ignoring cached checksums
--no-minifySkip minification
--no-bytecodeSkip bytecode compilation
--loglevel LEVELLog level: none, error, warn, info, debug. Default: debug
--jsonOutput 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.

sh
mikro deploy [ENTRY]
OptionDescription
ENTRYEntry file (default: main field in package.json)
-p, --port PORTSerial port (auto-detected if omitted)
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip auto-loading of .env and .env.production
--consoleAttach console after deploy and restart device
--recoverReset into safe mode before deploying. See Troubleshooting
--no-restartStage the build without restarting; it installs at the next device boot
--no-minifySkip minification
--loglevel LEVELLog level: none, error, warn, info, debug. Default: warn
--jsonOutput 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.

sh
mikro build [ENTRY] [-o DIR]
OptionDescription
ENTRYEntry file (default: main field in package.json)
-o, --out-dir DIROutput directory (default: .mikro/build in the project root)
--no-minifySkip minification
--no-bytecodeSkip bytecode compilation
--loglevel LEVELLog level: none, error, warn, info, debug. Default: warn
--jsonOutput 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.

sh
mikro flash
OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
--chip CHIPThe device's chip (for example esp32c6). Detected from the connected device if omitted
--board BOARDBoard 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 FEATURESFlash 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 DIRPath to a local ESP-IDF build directory
--from URLThe URL of a firmware archive (a .tar.gz, as mikro fw pack writes it), flashed as it is
--baud BAUDBaud rate for flashing (default: 460800)
-y, --yesSkip confirmation prompt
--forceFlash 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:

sh
pn mikro idf set-target esp32c6
pn mikro idf build flash monitor

When 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.

sh
pn mikro fw pack
OptionDescription
--out FILEOutput 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 BOARDIn 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 NIn 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.

sh
pn mikro fw build
OptionDescription
--board BOARDBuild 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 IMAGEBuild 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 NBuild 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
--flashThen 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.

sh
pn mikro fw list

mikro 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.

sh
pn mikro fw check

mikro ls ​

List connected devices. Each line shows the device's name, its serial port path, and details:

sh
mikro ls
swift-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.

OptionDescription
--jsonOutput 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.

sh
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
OptionDescription
-p, --port PORTDevice to act on (path, serial, or name). Defaults to the only connected device.
--jsonOutput 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.

sh
mikro console
OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
--rawPlain serial passthrough (no protocol framing)
--hexShow raw hex bytes
--recoverReset 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 (no logFile required).
  • mikro logs pull — read the on-device file written by the file logger (requires logFile in mikro.config.ts).
  • mikro logs reset — clear the on-device log files (requires logFile in mikro.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.

sh
mikro logs tail
OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
-r, --restartRestart the device first
--loglevelDrop 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.

sh
# Stream to stdout
mikro logs pull

# Pipe through tools
mikro logs pull | grep ERROR

# Archive both generations
mikro logs pull ./forensics
OptionDescription
DESTOptional directory to write files into
-p, --port PORTSerial 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.

sh
mikro logs reset
OptionDescription
-p, --port PORTSerial 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.

sh
mikro test [PATTERN]
OptionDescription
PATTERNGlob pattern to filter test files (default: **/*.test.ts)
-p, --port PORTSerial port (auto-detected if omitted)
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip auto-loading of .env and .env.test
--no-minifySkip minification
--no-bytecodeSkip bytecode compilation
-t, --timeout MSPer-file timeout in ms (default: 60000)
-u, --update-heapOverwrite committed heap snapshots with this run's measurements, boot figures included
--heap-tolerance SIZEHeap drift below which a snapshot is neither flagged nor rewritten
--diagnosticsShow per-test heap progress and supervisor announcements
--isolateRestart the device before every file so each starts from a fresh boot; slower, but run order cannot affect results or heap figures
-y, --yesSkip confirmation prompt
--jsonOutput as JSON

See Build options for details on --no-minify and other build flags.

sh
# Run all tests
mikro test

# Run a specific test file
mikro test 'test/smoke.test.ts'

# Run with env vars
mikro test --env-file .env

Test files import from mikro/test:

ts
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.

sh
mikro profile
  esp32c6  js 218KB   system 252.4KB

There are two ceilings, and either one can be the first you hit:

  • js: the JS budget left before mem_limit throws InternalError: 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.

OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
--writeRecord this run's reading as the committed boot snapshot
--heap-tolerance SIZEDrift below which the snapshot is neither flagged nor rewritten
--jsonOutput 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.

sh
mikro env list
OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
--jsonOutput as JSON

mikro env set ​

Set an environment variable on the device.

sh
mikro env set KEY [VALUE]
OptionDescription
KEYVariable name (max 15 characters)
VALUEVariable value. Allowed only with --no-secret. Without it, omit VALUE and the CLI prompts you for it
--no-secretPass VALUE as an argument and store it as non-secret (visible in env list). Requires a VALUE argument
-p, --port PORTSerial 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.

sh
# Secret: prompts for value (hidden input)
mikro env set API_KEY

# Non-secret: pass VALUE inline
mikro env set WIFI_SSID MyNetwork --no-secret

You 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.

sh
mikro env delete KEY
OptionDescription
KEYVariable name to delete
-p, --port PORTSerial 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.

sh
mikro ota setup
OptionDescription
--registry URLRegistry base URL (skips the url prompt)
--token TOKENRegistry token (with --registry, setup runs without prompts)
--userWrite ~/.mikro/registry.json (all projects) instead of the project's
--forceAccept 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.

sh
mikro ota pack
OptionDescription
--out FILEOutput path for the build (default: ./app-<version>.tgz)
--snapshotDerive a unique version so iteration needs no version bump (see below)
--no-minifySkip minification
--loglevel LEVELLog 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).

sh
mikro ota push
OptionDescription
--registry URLRegistry origin (default: .mikro/registry.json)
--tarball FILEPush a pre-packed .tgz instead of building
--release CHANNELAlso release the build to this channel (default: not served)
--token TOKENRegistry auth token (default: MIKRO_OTA_TOKEN env var)
--note TEXTFree-text note stored with the build (for example what changed)
--createCreate the app on first push instead of failing on an unknown app
--snapshotDerive 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.

sh
mikro ota release <version> <channel>
OptionDescription
VERSIONVersion of an already-published build to serve
CHANNELChannel to point at that build (for example beta or main)
--registry URLRegistry origin (default: .mikro/registry.json)
--token TOKENRegistry 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.

sh
mikro ota enroll
OptionDescription
--registry URLRegistry origin (default: .mikro/registry.json)
--token TOKENRegistry API token (default: MIKRO_OTA_TOKEN env var)
--name NAMEDisplay name stored with the device in the registry
--channel CHANNELRelease channel to enroll the device on (default: main)
--re-enrollRotate the update key when the device is already enrolled (the old one stops working)
--update-key SECRETWrite an externally issued update key to the device; the registry is not contacted
-p, --port PORTSerial port (auto-detected if omitted)
--jsonOutput as JSON

mikro clean ​

Remove the deployed app from the device. The device restarts and boots into the REPL with no app.

sh
mikro clean
OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
--fullRemove all files and environment variables, not just the deployed app
--recoverReset into safe mode before cleaning. See Troubleshooting
-y, --yesSkip 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.

sh
mikro erase
OptionDescription
-p, --port PORTSerial port (auto-detected if omitted)
--baud BAUDBaud rate (default: 460800)
-y, --yesSkip 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.

sh
mikro sim dev [ENTRY]
OptionDescription
ENTRYEntry file (default: main field in package.json)
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip auto-loading of .env and .env.simulator
--no-minifySkip minification
--no-bytecodeSkip bytecode compilation

mikro sim deploy ​

One-shot build + deploy to the simulator (no watch). The sim equivalent of mikro deploy.

sh
mikro sim deploy [ENTRY]
OptionDescription
ENTRYEntry file (default: main field in package.json)
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip auto-loading of .env and .env.simulator
-e, --eraseErase current app before uploading
--no-restartDo not restart sim after deploy
--no-minifySkip minification
--no-bytecodeSkip bytecode compilation
--jsonOutput as JSON

mikro sim repl ​

Open an interactive REPL session on a fresh sim process. The sim equivalent of mikro console.

sh
mikro sim repl

mikro sim test ​

Discover and run *.test.ts files in the simulator. The sim equivalent of mikro test.

sh
mikro sim test [PATTERN]
OptionDescription
PATTERNGlob pattern to filter test files (default: **/*.test.ts)
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip auto-loading of .env and .env.simulator
--no-minifySkip minification
--no-bytecodeSkip bytecode compilation
-t, --timeout MSPer-file timeout in ms (default: 60000)
-u, --update-heapOverwrite committed heap snapshots with this run's measurements
--heap-tolerance SIZEHeap drift below which a snapshot is neither flagged nor rewritten
--diagnosticsShow per-test heap progress and supervisor announcements
--jsonOutput as JSON

mikro sim env ​

Manage simulator environment variables. The sim equivalent of mikro env.

sh
mikro sim env list
mikro sim env get KEY
mikro sim env set KEY [VALUE] [--no-secret]
mikro sim env delete KEY

mikro sim clean ​

Remove the deployed app from the simulator (sim-fs/app/), leaving environment variables intact.

sh
mikro sim clean

mikro sim reset ​

Erase the entire simulator state: filesystem and environment variables.

sh
mikro sim reset [-y]
OptionDescription
-y, --yesSkip 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.

sh
mikro sim profile [ENTRY]
OptionDescription
ENTRYEntry file (default: main field in package.json)
--mem-limit BYTESQuickJS heap ceiling for the profile run (default: 32M)
--memory-budget KBHighlight rows against an explicit budget in KB
--chip NAMEPreset budget for a chip (for example esp32c6 or esp32s3)
--top NShow only the N largest modules
--sort KEYSort by size (default) or order (load order)
--min-bytes NHide modules smaller than N bytes
--include-nativeInclude native:* runtime modules (excluded by default)
--include-builtinsInclude mikro/* built-in modules (excluded by default)
--only-nativeShow only native:* modules
--only-builtinsShow only mikro/* built-ins
--jsonOutput as JSON
--env-file FILEExtra .env file, applied last (highest priority); see precedence
--no-auto-envSkip 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.

sh
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-minify disables minification. Useful for debugging; the output is larger but more readable in stack traces.
  • --minifier MINIFIER selects the minifier: esbuild (default), terser, or swc. Can also be set via build.minifier.
  • --minify-level LEVEL sets minification aggressiveness: default or max. Can also be set via build.minifyLevel.
  • --no-bytecode skips compiling JavaScript to QuickJS bytecode. The device will parse JavaScript source at runtime, which uses more memory and is slower to start.
  • --loglevel LEVEL sets 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. deploy and build default to warn and dev to debug, unless build.logLevel is 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:

sh
mkdir -p ~/.bash_completion.d
mikro completion bash > ~/.bash_completion.d/mikro
echo 'source ~/.bash_completion.d/mikro' >> ~/.bashrc
sh
mikro completion zsh > "${fpath[1]}/_mikro"
# reload completions in the current shell:
autoload -U compinit && compinit
sh
mikro completion fish > ~/.config/fish/completions/mikro.fish

Then start a fresh shell session and try:

sh
mikro <TAB>              # → dev, deploy, env, build, flash, ...
mikro dev --<TAB>        # → --port, --env-file, --no-minify, ...
mikro dev --port <TAB>   # → live serial devices on your machine

Completion 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:

sh
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.

Mikro.js is built with AI assistance, code and docs alike. Read the AI disclosure.