# Moonbit Agent Guide

> Guide for writing, refactoring, and testing MoonBit projects, including exclusive use of `.mbtx` for agent-authored automation. Use when working in MoonBit modules or packages, organizing MoonBit files, using moon tooling (build/check/run/test/doc/ide etc.), or following MoonBit-specific layout, scripting, documentation, and testing conventions.

- Skill: `moonbitlang/moonbit-agent-guide` (Agent Skill, multi-file: 9 files)
- Install (CLI): `npx skillmds@latest add moonbitlang/moonbit-agent-guide`
- Raw SKILL.md: https://api.skillmd.com/api/skills/moonbitlang/moonbit-agent-guide/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: moonbitlang (https://skillmd.com/u/moonbitlang)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/moonbitlang/moonbit-agent-guide

---


# MoonBit Task Checklist

- Locate the enclosing `moon.mod` and relevant `moon.pkg` files before editing.
- Discover APIs with `moon ide doc`; use `outline`, `peek-def`, `find-references`, `hover`, and `rename` for semantic navigation and refactoring.
- Use `.mbtx` exclusively for agent-authored automation; keep automation logic out of shell scripts and other scripting languages.
- Keep changes within the correct package. Files do not define namespaces; separate top-level items with `///|`.
- Add regression tests for bug fixes. Add black-box tests and docstring examples for new public APIs.
- Validate with `moon check` and the narrowest relevant `moon test`; use `moon test --update` only for intended snapshot changes. Use `moon explain --diagnostic` to list warnings and `--warn-list` to enable selected warnings (for example, `+unnecessary_annotation`).
- Run `moon fmt` and `moon info` before handoff. Review generated `.mbti` changes, especially when the public API should remain stable.


# MoonBit Project Layouts

MoonBit uses the `.mbt` extension for source code files and interface files with the `.mbti` extension. At
the top-level of a MoonBit project there is a `moon.mod` file specifying
the metadata of the project. The project may contain multiple packages, each
with its own `moon.pkg`. Subdirectories may also contain `moon.mod`
files indicating that a different set of dependencies can be used for that subdir.
Legacy projects may still contain `moon.mod.json`; treat it as the old module
metadata format and migrate/update guidance to `moon.mod` instead of creating
new `moon.mod.json` files.

## Example layout

```
my_module
├── moon.mod                  # Module metadata; source option can specify the source directory
├── moon.pkg             # Package metadata (each directory is a package like Golang)
├── README.mbt.md             # Markdown with tested code blocks (`test "..." { ... }`)
├── README.md -> README.mbt.md
├── cmd                       # Command line directory
│   └── main
│       ├── main.mbt
│       └── moon.pkg     # executable package with `options("is-main": true)`
├── liba/                     # Library packages
│   └── moon.pkg         # Referenced by other packages as `@username/my_module/liba`
│   └── libb/                 # Library packages
│       └── moon.pkg     # Referenced by other packages as `@username/my_module/liba/libb`
├── user_pkg.mbt              # Root packages, referenced by other packages as `@username/my_module`
├── user_pkg_wbtest.mbt       # White-box tests (only needed for testing internal private members, similar to Golang's package mypackage)
└── user_pkg_test.mbt         # Black-box tests
└── ...                       # More package files, symbols visible to current package (like Golang)
```

- **Module**: characterized by a `moon.mod` file in the project root directory.
  A MoonBit *module* is like a Go module; it is a collection of packages in subdirectories, usually corresponding to a repository or project.
  Module boundaries matter for dependency management and import paths.

- **Package**: characterized by a `moon.pkg` file in each directory.
  All subcommands of `moon` will
  still be executed in the directory of the module (where `moon.mod` is
  located), not the current package.
  A MoonBit *package* is the actual compilation unit (like a Go package).
  All source files in the same package are concatenated into one unit and
  thereby share all definitions throughout that package.
  The `name` in the `moon.mod` file combined with the relative path to
  the package source directory defines the package name, not the file name.
  Imports refer to module + package paths, NEVER to file names.

- **Files**:
  A `.mbt` file is just a chunk of source code inside a package.
  File names do NOT create modules, packages, or namespaces.
  You may freely split/merge/move declarations between files in the same package.
  Any declaration in a package can reference any other declaration in that package, regardless of file.


## Coding/layout rules you MUST follow:

1. Prefer many small, cohesive files over one large file.
   - Group related types and functions into focused files (e.g. http_client.mbt, router.mbt).
   - If a file is getting large or unfocused, create a new file and move related declarations into it.

2. You MAY freely move declarations between files inside the same package.
   - Each block is separated by `///|`. Moving a function/struct/trait between files does not change semantics, as long as its name and pub-ness stay the same. The order of each block is irrelevant too.
   - It is safe to refactor by splitting or merging files inside a package.

3. File names are purely organizational.
   - Do NOT assume file names define modules, and do NOT use file names in type paths.
   - Choose file names to describe a feature or responsibility, not to mirror type names rigidly.

4. When adding new code:
   - Prefer adding it to an existing file that matches the feature.
   - If no good file exists, create a new file under the same package with a descriptive name.
   - Avoid creating giant "impl", “misc”, or “util” files.

5. Tests:
   - Place tests in dedicated test files (e.g. `*_test.mbt`) within the appropriate package.
     For a package (besides `*_test.mbt`files), `*.mbt.md` files are also blackbox test files in addition to Markdown files.
     The code blocks (separated by triple backticks) `mbt check` are treated as test cases and serve both purposes: documentation and tests.
     You may have `README.mbt.md` files with `mbt check` code examples. You can also symlink `README.mbt.md` to `README.md`
     to make it integrate better with GitHub.
   - It is fine — and encouraged — to have multiple small test files.

6. Interface files (`pkg.generated.mbti`)
   `pkg.generated.mbti` files are compiler-generated summaries of each package's public API surface.
   They provide a formal, concise overview of all exported types, functions, and traits without implementation details.
   They are generated using `moon info` and useful for code review. When you have a commit that does not change public APIs, `pkg.generated.mbti` files will remain unchanged, so it is recommended to put `pkg.generated.mbti` in version control when you are done.
   Do not modify `pkg.generated.mbti` directly, including whitespace-only cleanup; regenerate it with `moon info` and review its diff as the public API signal.

   For IDE navigation and symbol lookup commands, see the dedicated `moon ide` section below.

# Common Pitfalls to Avoid

- **Don't use uppercase for variables/functions** - compilation error
- **Don't forget `mut` for mutable record fields** - immutable by default (note that Arrays typically do NOT need `mut` unless completely reassigning to the variable - simple push operations, for example, do not need `mut`)
- **Don't ignore error handling** - either handle errors explicitly, or declare `raise` on the caller and let checked errors propagate
- **Don't use `return` unnecessarily** - the last expression is the return value
- **Don't create methods without Type:: prefix** - methods need explicit type prefix
- **Don't forget to handle array bounds** - use `get()` for safe access
- **Don't forget @package prefix when calling functions from other packages**
- **Don't use ++ or -- (not supported)** - use `i = i + 1` or `i += 1`
- **Don't add explicit `try` for error propagation** - inside a `raise` function, call error-raising functions normally; use `catch` to handle locally and `try!` only when aborting is intended
- **Legacy syntax**: Legacy code may use `function_name!(...)` or `function_name(...)?` - these are deprecated; use normal calls for propagation.
- **Don't write an empty parameter list for `main`** - use `fn main { ... }` or `fn main raise { ... }`, not `fn main() { ... }` or `fn main() raise ... { ... }`
- **Don't write record-style enum or error constructor fields** - labeled constructor fields use `label~ : Type`, e.g. `InvalidNumber(input~ : String)`, not `InvalidNumber(input: String)`
- **Prefer range `for` loops over C-style** - `for i in 0..<(n-1) {...}` and `for j in 0..=6 {...}` are more idiomatic in MoonBit
- **Don't use `for { ... }` for infinite loops** - write `for ;; { ... }` instead
- **Don't `derive(Show)` for debugging** - derive `Debug` and use `debug_inspect()` for test/diagnostic output (`\{Repr(value)}` for interpolation of composed values). Reserve a manual `impl Show` for specialized display formats (JSON, XML, domain text)
- **Don't call `@json.inspect()`** - use the prelude `json_inspect(value, ...)` without a package prefix
- **Async** - MoonBit has no `await` keyword; do not add it. Async functions default to raising, so do not add `raise`; add `noraise` only when the async body must not raise.
  Async functions and tests are characterized by those which call other async functions.
  To identify a function or test as async, simply add the `async` prefix (e.g. `[pub] async fn ...`, `async test ...`).

# `moon` Essentials

## Script Mode (`.mbtx`)

Use `.mbtx` exclusively for agent-authored automation. Keep loops,
conditionals, parsing, transformation, and process orchestration in MoonBit
instead of shell scripts or another scripting language. The outer shell should
only launch the script or run a direct, single-purpose command.

An `.mbtx` file is an optional `import { ... }` block followed by ordinary
MoonBit code, including a `main` function. Run it directly with:

```bash
moon run path/to/script.mbtx [args...]
```

Choose `main` by the effects the script actually uses:

- `fn main { ... }` is synchronous and does not propagate errors.
- `fn main raise { ... }` is synchronous and may propagate checked errors.
- `async fn main { ... }` is for scripts that call async APIs. Import
  `"moonbitlang/async"`; do not add `raise` or `await`.

All three work on the default Wasm target. Do not mark a purely synchronous
script `async`; MoonBit reports `unused_async`.

When automation runs commands, import `"moonbitlang/async/shell"` and use
`@shell.Cmd` or `@shell.Pipeline`. They keep the executable and arguments
separate and never invoke a shell, so characters such as `|`, `$()`, and `*`
are passed literally. Use ordinary MoonBit control flow instead of `&&`, shell
loops, or command substitution.

Script mode defaults to Wasm. Supplying `--wasm-policy policy.json` enables
deny-by-default control over MoonBit host APIs; grant only the required
filesystem, environment, network, or process access. For process automation,
prefer a narrow `process.allow` rule with an exact program and argument prefix
over `process.spawn: true`. The policy authorizes a child process but does not
sandbox the child itself, so the host must also confine child processes when
they are not trusted.

Minimal script (`hello.mbtx`):

```mbtx
fn main {
  println("Hello from MoonBit script mode")
}
```

Command-line automation (`sum_args.mbtx`):

```mbtx
import {
  "moonbitlang/core/env",
  "moonbitlang/core/string",
}

fn main raise {
  let args = @env.args()
  for arg in args[1:]; total = 0 {
    continue total + @string.parse_int(arg)
  } nobreak {
    println(total)
  }
}
```

Running `moon run sum_args.mbtx 10 20 12` prints `42`.

Package imports work directly in the script header (`format_json.mbtx`):

```mbtx
import {
  "moonbitlang/core/json",
}

fn main raise {
  let source =
    #|{
    #|  "project": "moonbit",
    #|  "enabled": true
    #|}
  let value = @json.parse(source)
  println(value.stringify(indent=2))
}
```

Async automation works on the default Wasm target. For example, run
`moon run async_job.mbtx` for:

```mbtx
import {
  "moonbitlang/async",
}

async fn main {
  @async.sleep(10)
  println("async automation complete")
}
```

## Essential Commands

- `moon new my_project` - Create new project
- `moon run cmd/main` - Run main package
- `moon run - < hello.mbtx` - Run script code from stdin (useful for quick experiments)
- `moon run -e "code snippet"` - Run code from command line argument (good for one-liners)
  ```
  moon run -e 'fn main { println("Hello, MoonBit!") }'
  ```
- `moon build` - Build project
  (`moon run` and `moon build` both support `--target`; `moon build` also supports `--diagnostic-limit <N>`)
- `moon check` - Type check without building, use it REGULARLY, it is fast
  (`moon check` also supports `--target` and `--diagnostic-limit <N>`)
- `moon info` - Type check and generate `mbti` files.
  Run it to see if any public interfaces changed.
  (`moon info` also supports `--target`.)
- `moon check --target all` - Type check for all backends
  Process structured diagnostics with a saved `.mbtx` script rather than a
  shell pipeline or another scripting language. For example, save this as
  `filter_diagnostics.mbtx`; `@shell.Cmd::each_line` runs `moon check` directly,
  streams its JSON output, and returns its exit status:
  ```mbtx
import {
  "moonbitlang/async",
  "moonbitlang/async/shell",
  "moonbitlang/core/json",
}

async fn main {
  let seen : Map[String, Unit] = Map([])
  let exit_code = @shell.Cmd(
    "moon",
    ["check", "--target", "all", "--output-json"],
  ).each_line(line => {
      try @json.parse(line) catch {
        _ => ()
      } noraise {
        { "level": "warning", "path": String(path), .. } =>
          if !seen.contains(path) {
            seen[path] = ()
            println(path)
          }
        _ => ()
      }
    })
  if exit_code != 0 {
    fail("moon check exited with code \{exit_code}")
  }
}
  ```
  Run it with a policy that allows that command prefix:
  ```json
  {
    "process": {
      "allow": [
        {
          "program": "moon",
          "args_prefix": ["check", "--target", "all", "--output-json"]
        }
      ]
    }
  }
  ```
  ```bash
  moon run --wasm-policy moon-check-policy.json filter_diagnostics.mbtx
  ```
- `moon explain` - Show built-in documentation for compiler diagnostics and language topics.
  - `moon explain --diagnostic` lists warning mnemonics and IDs.
  - `moon explain --diagnostic 31` explains warning 31 (`unused_optional_argument`).
  - `moon explain --diagnostic unused_optional_argument` explains the same warning by mnemonic.
  - `moon explain --attribute` lists supported attributes such as `#deprecated`, `#alias`, `#cfg`, `#coverage.skip`, and `#warnings`.
  - `moon explain --attribute deprecated` explains the `#deprecated` attribute and its supported forms.
- `moon add package` - Add dependency
- `moon remove package` - Remove dependency
- `moon fmt` - Format code - should be run periodically - note that the files may be rewritten
Note you can also use `moon -C dir check` to run commands in a specific directory.

### Profiling Hot Paths (`moon run --profile`)

`moon run --profile --target native --release cmd/<main>` runs a native release build under a sampling profiler and prints ranked **self-time** and **inclusive-time** tables plus a "runtime leaf costs attributed to MoonBit callers" section (which maps allocation, reference-counting, and string-equality costs back to *your* functions), alongside a `profile.json` and a `.trace` you can open in Instruments. On macOS it needs Xcode's `xcrun xctrace`, so install the full Xcode (not just the command-line tools) first. A single parse or compute is far too short to sample meaningfully, so point the profiled `main` at a loop that exercises the hot path a few hundred times over a representative fixture; this loop harness is throwaway and should never be committed.

Read **self-time** for *which function burns cycles* and **inclusive-time** for *which call subtree dominates*, then work a tight loop: profile, fix the top item, re-profile. Always re-baseline before trusting a delta — sampled timings drift with machine load, so build and benchmark the branch and `main` back-to-back (interleaved) rather than comparing against a number from an earlier session.

### Test Commands

- `moon test` - Run all tests
  (`moon test` also supports `--target`)
- `moon test --update` - Update snapshots
- `moon test -v` - Verbose output with test names
- `moon test [dirname|filename]` - Test specific directory or file
- `moon coverage analyze` - Analyze coverage
- `moon test [dirname|filename] --filter 'glob'` - Run tests matching filter
  ```
  moon test float/float_test.mbt --filter "Float::*"
  moon test float -F "Float::*" // shortcut syntax
  ```

## `README.mbt.md` Generation Guide

- Output `README.mbt.md` in the package directory.
  `*.mbt.md` file and docstring contents treats `mbt check` specially.
  `mbt check` block will be included directly as code and also run by `moon check` and `moon test`.  If you don't want the code snippets to be checked, explicit `mbt nocheck` is preferred.
  If you are only referencing types from the package, you should use `mbt nocheck` which will only be syntax highlighted.
  Symlink `README.mbt.md` to `README.md` to adapt to systems that expect `README.md`.

## Testing Guide

Use snapshot tests as it is easy to update when behavior changes.

- **Snapshot Tests**: write `inspect(value)` / `debug_inspect(value)` / `json_inspect(value)`, then run `moon test --update` (or `moon test -u`) to fill in `content=`.
  - Use `inspect()` for values that implement `Show` (primitives, or types with a manual `impl Show`).
  - Use `debug_inspect()` for any type that derives `Debug` — the default for your own data types.
  - Use `json_inspect()` for complex nested structures (uses the `ToJson` trait, produces more readable output).
  - It is encouraged to inspect the whole return value of a function if it is not huge; this keeps the test simple. Derive `Debug` and/or `ToJson` (or `impl Show`) on `YourType` accordingly.
- **Update workflow**: After changing code that affects output, run `moon test --update` to regenerate snapshots, then review the diffs in your test files (the `content=` parameter will be updated automatically).
- **Validation order**: Follow the `MoonBit Task Checklist`.

- Black-box by default: Call only public APIs via `@package.fn`. Use white-box tests only when private members matter.
- Grouping: Combine related checks in one `test "..." { ... }` block for speed and clarity.
- Panics: Name tests with prefix `test "panic ..." {...}`; if the call returns a value, wrap it with `ignore(...)` to silence warnings.
- Errors: For expected success, call error-raising functions directly. If a call unexpectedly raises, the test fails with the actual error. For expected failure, use `try ... catch ... noraise`, inspect the error in `catch`, and fail explicitly in `noraise`.
  Default expected-failure shape: `try f() catch { err => inspect(err) } noraise { _ => fail("expected to fail") }`.

### Docstring tests

Public APIs are encouraged to have docstring tests.

````mbt check
///|
/// Return the sum of an `Array`.
///
/// # Example
/// ```mbt check
/// test {
///   inspect(sum_array([1, 2, 3, 4, 5, 6]), content="21")
/// }
/// ```
pub fn sum_array(xs : Array[Int]) -> Int {
  xs.fold(init=0, (a, b) => a + b)
}
````

The MoonBit code in a docstring will be type checked and tested automatically
(using `moon test --update`). In docstrings, `mbt check` should only contain `test` or `async test`.

## Spec-driven Development

- The spec can be written in a readonly `spec.mbt` file (name is conventional, not mandatory) with stub code marked as declarations:

```mbt check
///|
declare pub type Yaml

///|
declare pub fn Yaml::to_string(y : Yaml) -> String raise

///|
declare pub impl Eq for Yaml

///|
declare pub fn parse_yaml(s : String) -> Yaml raise
```

- Add `spec_easy_test.mbt`, `spec_difficult_test.mbt`, etc. to test the spec functions; everything will be type-checked(`moon check`).
- The AI or users can implement the `declare` functions in different files thanks to our package organization.
- Run `moon test` to check everything is correct.

- `declare` is supported for functions, methods, and types.
- The `pub type Yaml` line is an intentionally opaque placeholder; the implementer chooses its representation.
- Note the spec file can also contain normal code, not just declarations.

## `moon ide [doc|peek-def|outline|find-references|hover|rename|analyze]` for code navigation and refactoring

For project-local symbols and navigation, use:
- `moon ide doc <query>` to discover available APIs, functions, types, and methods in MoonBit. Always prefer `moon ide doc` over other approaches when exploring what APIs are available, it is **more powerful and accurate** than `grep_search` or any regex-based searching tools.
- `moon ide outline .` to scan a package,
- `moon ide find-references <symbol>` to locate usages, and
- `moon ide peek-def` for inline definition context and to locate toplevel symbols.
- `moon ide hover sym --loc filename:line:col` to get type information at a specific location.
- `moon ide rename <symbol> <new_name> [--loc filename:line:col]` to rename a symbol project-wide. Prefer `--loc` when symbol names are ambiguous.
- `moon ide analyze [path]` to inspect public API usage of a package or module when planning safe refactors.
These tools save tokens and are more precise than grepping (`grep` displays results in both definitions and call sites including comments too).

### `moon ide doc` for API Discovery

`moon ide doc` uses a specialized query syntax designed for symbol lookup:
- **Empty query**: `moon ide doc ''`

  - In a module: shows all available packages in current module, including dependencies and moonbitlang/core
  - In a package: shows all symbols in current package
  - Outside package: shows all available packages

- **Function/value lookup**: `moon ide doc "[@pkg.]value_or_function_name"`

- **Type lookup**: `moon ide doc "[@pkg.]Type_name"` (builtin type does not need package prefix)

- **Method/field lookup**: `moon ide doc "[@pkg.]Type_name::method_or_field_name"`

- **Package exploration**: `moon ide doc "@pkg"`
  - Show package `pkg` and list all its exported symbols
  - Example: `moon ide doc "@json"` - explore entire `@json` package
  - Example: `moon ide doc "@encoding/utf8"` - explore nested package
- **Multiple queries**: `moon ide doc "query1" "query2" ...`
  - Run multiple queries in one invocation and combine results
  - Example: `moon ide doc "String" "Array" "@json"` to explore multiple types and a package at once
- **Globbing**: Use `*` wildcard for partial matches, e.g. `moon ide doc "String::*rev*"` to find all String methods with "rev" in their name

#### `moon ide doc` Examples

````bash
# search for String methods in standard library:
$ moon ide doc "String"

type String

  pub fn String::add(String, String) -> String
  # ... more methods omitted ...

$ moon ide doc "@buffer" # list all symbols in package buffer:
moonbitlang/core/buffer

fn from_array(ArrayView[Byte]) -> Buffer
# ... omitted ...

$ moon ide doc "@buffer.new" # list the specific function in a package:
package "moonbitlang/core/buffer"

pub fn new(size_hint? : Int) -> Buffer
  Creates ... omitted ...


$ moon ide doc "String::*rev*"  # globbing
package "moonbitlang/core/string"

pub fn String::rev(String) -> String
  Returns ... omitted ...
  # ... more

pub fn String::rev_find(String, StringView) -> Int?
  Returns ... omitted ...
````

**Best practice**: Treat this section as command reference; validation is defined in the `MoonBit Task Checklist`.

### `moon ide rename sym new_name [--loc filename:line:col]` example

When the user asks: "Can you rename the function `compute_sum` to `calculate_sum`?"

```
$ moon ide rename compute_sum calculate_sum --loc math_utils.mbt:2

*** Begin Patch
*** Update File: cmd/main/main.mbt
@@
 ///|
 fn main {
-  println(@math_utils.compute_sum(1, 2))
+  println(@math_utils.calculate_sum(1, 2))
 }
*** Update File: math_utils.mbt
@@
 ///|
-pub fn compute_sum(a: Int, b: Int) -> Int {
+pub fn calculate_sum(a: Int, b: Int) -> Int {
   a + b
 }
*** Update File: math_utils_test.mbt
@@
 ///|
 test {
-  inspect(@math_utils.compute_sum(1, 2))
+  inspect(@math_utils.calculate_sum(1, 2))
 }
*** End Patch
```

### `moon ide hover sym --loc filename:line:col` example

When the user asks: "What is the signature and docstring of `filter`? at line 14 of hover.mbt"

```
$ moon ide hover  filter --loc hover.mbt:14
test {
  let a: Array[Int] = [1]
  inspect(a.filter((x) => {x > 1}))
            ^^^^^^
            ```moonbit
            fn[T] Array::filter(self : Array[T], f : (T) -> Bool raise?) -> Array[T] raise?
            ```
            ---
            
             Creates a new array containing all elements from the input array that satisfy
             ... omitted ...
}
```


### `moon ide peek-def sym [--loc filename:line:col]` example

When the user asks: "Can you check if `Parser::read_u32_leb128` is implemented correctly?"
you can run `moon ide peek-def Parser::read_u32_leb128` to get the definition context
(this is better than `grep` since it searches the whole project by semantics):

``` file src/parse.mbt
L45:|///|
L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
L47:|  ...
...:| }
```

Now if you want to see the definition of the `Parser` struct, you can run:

```bash
$ moon ide peek-def Parser --loc src/parse.mbt:46:4
Definition found at file src/parse.mbt
  | ///|
2 | priv struct Parser {
  |             ^^^^^^
  |   bytes : Bytes
  |   mut pos : Int
  | }
  |
```

For the `--loc` argument, the line number must be precise; the column can be approximate since
the positional argument `Parser` helps locate the position.

If the "sym" is a toplevel symbol, the location can be omitted:

````bash
$ moon ide peek-def String::rev
Found 1 symbols matching 'String::rev':

`pub fn String::rev` in package moonbitlang/core/builtin at /Users/usrname/.moon/lib/core/builtin/string_methods.mbt:1039-1044
1039 | ///|
     | /// Returns a new string with the characters in reverse order. It respects
     | /// Unicode characters and surrogate pairs but not grapheme clusters.
     | pub fn String::rev(self : String) -> String {
     |   self[:].rev()
     | }
````

### `moon ide outline [dir|file]` and `moon ide find-references <sym>` for Package Symbols

Use `moon ide outline` to scan a package or file for top-level symbols and locate usages without grepping.

- `moon ide outline dir` outlines the current package directory (per-file headers)
- `moon ide outline parser.mbt` outlines a single file
  This is useful when you need a quick inventory of a package, or to find the right file before `peek-def`.
- `moon ide find-references TranslationUnit` finds all references to a symbol in the current module

```bash
$ moon ide outline .
spec.mbt:
 L003 | pub(all) enum CStandard {
        ...
 L013 | pub(all) struct Position {
        ...
```

```bash
$ moon ide find-references TranslationUnit
```

## Package Management

### Adding Dependencies

```sh
moon add moonbitlang/x        # Add latest version
moon add moonbitlang/x@0.4.6  # Add specific version
```

### Updating Dependencies

```sh
moon update                   # Update package index
```

### Browsing Third-Party Source (`moon fetch`)

`moon fetch <author>/<module>[@<version>]` downloads a package's source into `.repos/<author>/<module>/<version>/` for offline reading (examples, internals, generated `.mbti`). It does NOT add the package to `moon.mod` — use `moon add` for that. Add `.repos/` to `.gitignore`.

```sh
moon fetch moonbitlang/async@0.18.1   # browse source/examples without taking a dependency
```

### Typical Module configurations (`moon.mod`)
```
name = "username/hello"
version = "0.1.0"
readme = "README.mbt.md"
repository = ""
license = "Apache-2.0"
keywords = []
description = "..."

import {
  "moonbitlang/x@0.4.6",
}

options(
  // source: "src", // Optional; default is "."
  "preferred-target": "native",
)
```

Use `moon add moonbitlang/x@0.4.6` and `moon remove moonbitlang/x` to manage the `import` block instead of editing dependency versions by hand.

### Typical Package configuration (`moon.pkg`)

moon.pkg for simplicity
```
import {
  "username/hello/liba",
  "moonbitlang/x/encoding" @libb,
}
import {
  "username/hello/test_helpers",
} for "test"
import {
  "username/hello/internal_test_helpers",
} for "wbtest"
options(
  "is-main": true,
)
```

Use `supported_targets = "native"` or another target-set expression at top level when the whole package only supports selected backends.

```
supported_targets = "native"
options(
  "is-main": true,
)
```

Packages are per directory and packages without a `moon.pkg` file are not recognized.

### Package Importing (used in moon.pkg)

- **Import format**: `"module_name/package_path"`
- **Usage**: `@alias.function()` to call imported functions
- **Default alias**: Last part of path (e.g., `liba` for `username/hello/liba`)
- **Package reference**: Use `@packagename` in test files to reference the
  tested package

**Package Alias Rules**:

- Import `"username/hello/liba"` → use `@liba.function()` (default alias is the last path segment)
- Import with custom alias `import { "moonbitlang/x/encoding" @enc}` → use `@enc.function()`
  (Note that this is unnecessary when the last path segment is identical to the alias name.)
- In `_test.mbt` or `_wbtest.mbt` files, the package being tested is auto-imported

Example:

```mbt nocheck
///|
/// In main.mbt after importing "username/hello/liba" in `moon.pkg`
fn main {
  println(@liba.hello()) // Calls hello() from liba package
}
```

### Using the Standard Library (moonbitlang/core)

The `moonbitlang/core` module is always available without adding it to `moon.mod` dependencies. Ordinary core packages still need explicit `moon.pkg` imports for package aliases such as `@utf8`, `@json`, or `@strconv`; add imports like `"moonbitlang/core/encoding/utf8"` when the compiler reports a missing or implicit core package.


### Creating Packages

To add a new package `fib` under `.`:

1. Create directory: `./fib/`
2. Add `./fib/moon.pkg`
3. Add `.mbt` files with your code
4. Import in dependent packages:

   ```
     import {
       "username/hello/fib",
     }
   ```

For more advanced topics like `conditional compilation`, `link configuration`, `warning control`, and `pre-build commands`, see `references/advanced-moonbit-build.md`.

## Async IO

Asynchronous programming uses compiler support plus the `moonbitlang/async`
runtime. `async fn main` works on the default Wasm target as well as native when
the runtime is imported. Individual host I/O APIs may still be target-specific;
use `moon ide doc "@async"` and validate on the intended target.

User-facing subpackages include `@async` (tasks, timers, cancellation),
`@async/aqueue`, `@async/fs`, `@async/shell` (shell-free process orchestration),
`@async/stdio`, and `@async/websocket`.
Each must be imported separately in `moon.pkg` or an `.mbtx` import block.

1. Add the dependency with `moon add moonbitlang/async`.
2. In the executable's `moon.pkg`, set `is-main` and import what you need:
   ```
   import {
     "moonbitlang/async",
     "moonbitlang/async/stdio",
   }
   options(
     "is-main": true,
   )
   ```
3. Define `async fn main` (not `fn main`). Spawn concurrent tasks via `with_task_group` for structured concurrency:
   ```mbt nocheck
   ///|
   async fn main {
     @async.with_task_group(group => {
       group.spawn_bg(() => {
         @async.sleep(50)
         @stdio.stdout.write("A\n")
       })
       group.spawn_bg(() => {
         @async.sleep(20)
         @stdio.stdout.write("B\n")
       })
     })
   }
   ```

- Async functions have a raising effect by default. Write `async fn main { ... }` or `async fn f(...) { ... }`, not `async fn main raise { ... }`.
- Use `async fn f(...) noraise { ... }` only when the async body must not raise. A `noraise` async function cannot call fallible APIs unless it handles their errors locally.

**Structured-concurrency contract for `with_task_group`:**

- When `with_task_group` returns, every task spawned in the group is guaranteed to have terminated — no orphan tasks, no resource leaks.
- If any spawned task fails (and was spawned without `allow_failure=true`), the whole group fails: every other task in the group is cancelled, and the error propagates out of `with_task_group`.
- Cancelled tasks are not considered failures; they raise a cancellation error but don't trigger peer cancellation.

**Closure syntax for `spawn_bg` / `spawn`:**

- ✅ `() => { ... }` — idiomatic; async-ness is inferred from context.
- ✅ `async fn() { ... }` — explicit annotation; equivalent to the arrow form.
- ⚠️ `fn() { ... }` — triggers `Warning [0027] deprecated_syntax`: "this `fn` is asynchronous but not annotated with `async`". Don't use.
- ❌ `async () => ...`, `fn() async { ... }`, `fn(args) async { ... }` — all parse errors. `async` only goes before `fn`, never before an arrow lambda or after a parameter list.

### Async tests

Use `async test` for tests that call async functions. The package containing the test must import `moonbitlang/async` for the test mode; import any async subpackages used by the test in the same `for "test"` block.

```
import {
  "moonbitlang/async",
  "moonbitlang/async/stdio",
} for "test"
```

```mbt nocheck
///|
async test "sleep completes" {
  @async.sleep(1)
  inspect("done", content="done")
}
```

- There is no `await` keyword (similar to functions that raise errors). Inside an `async test`, call async functions normally.
- `async test` also has the async raising effect by default; do not add `raise`.
- Async tests run in parallel by default. Avoid shared ports, files, environment variables, and global mutable state unless each test isolates its resources.
- Run `moon test` on the intended target; add `--target native` only when the
  APIs under test require native execution. Use `moon test -v` when checking
  test names or async scheduling behavior.
- In `README.mbt.md` and docstrings, `mbt check` blocks may contain `async test` blocks; make sure the package imports `moonbitlang/async` for the relevant test mode.

# MoonBit Language Tour

## Core facts

- **Expression‑oriented**: `if`, `match`, loops return values; the last expression is the return value.
- **References by default**: Arrays/Maps/structs mutate via reference; use `Ref[T]` for primitive mutability.
- **Blocks**: Separate top‑level items with `///|`. Generate code block‑by‑block.
  If a blank line is desired within a block (enclosed by curly braces), add a comment line after the blank line (with or without comment text).
- **Visibility**: `fn` is private by default; `pub` exposes read/construct as allowed; `pub(all)` allows external construction.
- **Naming convention**: lower_snake for values/functions; UpperCamel for types/enums; enum variants start UpperCamel.
- **Packages**: No `import` in code files; call via `@alias.fn`. Configure imports in `moon.pkg`.
- **Placeholders**: `...` is a valid placeholder in MoonBit code for incomplete implementations.
- **Global values**: immutable by default and generally require type annotations.
- **Garbage collection**: MoonBit has a GC, there is no lifetime annotation, there's no ownership system.
  Unlike Rust, like F#, `let mut` is only needed when you want to reassign a variable, not for mutating fields of a struct or elements of an array/map.

## MoonBit Error Handling (Checked Errors)

MoonBit uses checked error-throwing functions, not unchecked exceptions. All errors are a subtype of `Error`, and you can declare your own error types using `suberror`.

Checked errors are tracked in function signatures, not marked at every call site. A function that may raise declares `raise` or `raise SomeError`. If the caller only wants to pass that error upward, the caller also declares a compatible `raise` and calls the raising function normally.

- Plain call inside a `raise` function: propagate automatically.
- `fn main raise { ... }` is valid for synchronous command-line probes and small examples that should propagate errors. For async entry points, use `async fn main { ... }`; async functions can raise by default.
- In `suberror` constructors, labeled payloads use `label~ : Type`; call and pattern-match them with `label=value`.
- `expr catch { ... }` or `try { ... } catch { ... }`: handle explicitly.
- `try! expr`: abort if an error is raised.

Do not add Swift-style `try` for propagation. Do not use legacy `function_name!(...)` or `function_name(...)?` syntax for new code.

```mbt check
///|
/// Declare error types with 'suberror'
suberror ValueError {
  ValueError(String)
}

///|
/// Tuple struct to hold position info
struct Position(Int, Int) derive(ToJson, Debug, Eq)

///|
/// ParseError is subtype of Error
pub(all) suberror ParseError {
  InvalidChar(pos~ : Position, Char) // pos is labeled
  InvalidEof(pos~ : Position)
  InvalidNumber(pos~ : Position, String)
  InvalidIdentEscape(pos~ : Position)
} derive(Eq, ToJson, Debug)

///|
/// Functions declare what they can throw
fn parse_int(s : String, position~ : Position) -> Int raise ParseError {
  // 'raise' throws an error
  if s is "" {
    raise ParseError::InvalidEof(pos=position)
  }
  ... // parsing logic
}

///|
/// Declare a specific error type when callers should handle it precisely
fn div(x : Int, y : Int) -> Int raise ValueError {
  if y is 0 {
    raise ValueError::ValueError("Division by zero")
  }
  x / y
}

///|
test "expected success calls directly" {
  inspect(div(6, 3), content="2")
}

///|
test "expected failure handles the raised error" {
  try div(1, 0) catch {
    ValueError::ValueError(message) => inspect(message, content="Division by zero")
  } noraise {
    _ => fail("expected to fail")
  }
}

// Three ways to handle errors:

///|
/// Propagate automatically
fn use_parse(s : String, position~ : Position) -> Int raise ParseError {
  // This plain call is the correct propagation syntax.
  // `try! parse_int(...)` would abort instead of propagating.
  let x = parse_int(s, position~) // label punning, equivalent to position=position
  // Error auto-propagates by default.
  // Unlike Swift, you do not need to mark `try` for functions that can raise
  // errors; the compiler infers it automatically. This keeps error handling
  // explicit but concise.
  x * 2
}

///|
/// Use try! to abort if it raises, no raise in the signature 
fn use_parse2(position~ : Position) -> Int {
  let x = try! parse_int("123", position~) // label punning
  x * 2
}

///|
/// Handle with try-catch
fn handle_parse(s : String, position~ : Position) -> Int {
  parse_int(s, position~) catch {
    ParseError::InvalidEof(pos=_) => {
      println("Parse failed: InvalidEof")
      -1 // Default value
    }
    _ => 2
  }
}
```

Important: When calling a function that can raise errors, if you only want to
propagate the error, you do not need any marker; the compiler infers it.
Async functions automatically can raise errors without explicitly stating this. Do not add `raise` to async functions for propagation; add `noraise` only when the async function must reject unhandled errors.

## Integer, Char and overloaded literals

MoonBit supports `Byte`, `Int16`, `Int`, `UInt16`, `UInt`, `Int64`, `UInt64`, etc.
When the type is known, the literal can be overloaded:

```mbt check
///|
test "integer and char literal overloading disambiguation via type in the current context" {
  let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
    1, 1, 1, 1, 1,
  )
  // The literal `1` is overloaded based on the expected type in the current context.
  // compile time error if the literal cannot be represented in the target type, 
  // e.g. let a7 : Byte = 256 // ❌ won't compile, 256 exceeds Byte max value 255
  assert_eq(int, uint16.to_int())
  let (a1, a2, a3) : (Int, Char, UInt16) = ('b', 'b', 'b')
  // char literal overloading, `a1` will be the unicode value of 'b', 
  // compile time error when the literal cannot be represented in the target type 
  // e.g, let a6 : UInt16 = '𐍈' // ❌ 

…(truncated)
