**Registry modules:**
```mlld
import { @sortBy, @unique } from @mlld/array
import @corp/utils as @corp
>> With version
import { @helper } from @alice/utils@1.0.0
import { @helper } from @alice/utils@^1.0.0 >> semver range
```
**Local files (selected exports):**
```mlld
import { @helper } from "./utils.mld"
import { @config } from <@root/config.mld>
import { @prompt } from "../prompts/main.mld"
import {
@renderHeader,
@renderBody
} from "./templates.mld"
```
**Local files (namespace import):**
```mlld
import "./utils.mld" as @utils
show @utils.helper("report")
```
**Path resolution:**
- `./` and `../` paths resolve from the importing file's directory, not the shell cwd.
- `<@root/...>` resolves from the project root.
**Namespace imports:**
```mlld
import @alice/utils as @alice
import @bob/utils as @bob
show @alice.format(@data)
show @bob.format(@data) >> no collision
```
**Directory imports:**
```mlld
import "@agents" as @agentRegistry
show @agentRegistry.alice.tldr
show @agentRegistry.support.helper.name
>> With options
import "./agents" as @agents with { skipDirs: [] }
```
Directories auto-load `*/index.mld`. Default `skipDirs: ["_*", ".*"]`.
Use directory imports when you want a namespace object.
If you want selected exports, import the entry file directly:
```mlld
import { @helper } from "./agents/index.mld"
```
Relative directory paths resolve from the importing file's directory, not the shell cwd.
**Template collections** import an entire directory of `.att` files as a callable namespace:
```mlld
import templates from "@root/agents" as @agents(message, context)
>> Access templates by name (bracket or dot notation)
show @agents["alice"](@msg, @ctx) >> agents/alice.att
show @agents.support["helper"](@msg, @ctx) >> agents/support/helper.att
```
**Directory structure example:**
```
agents/
alice.att >> @agents["alice"](msg, ctx)
bob.att >> @agents["bob"](msg, ctx)
support/
helper.att >> @agents.support["helper"](msg, ctx)
escalate.att >> @agents.support["escalate"](msg, ctx)
```
**Key rules:**
- All templates in a collection share the same parameter signature
- Filenames with hyphens become underscores: `json-pretty.att` → `@tpl["json_pretty"]`
- Use dot notation for directories, brackets for template names
- Collections require parameters in the `as @name(params)` clause
**When to use collections:**
- Multiple templates with the same interface (agent prompts, formatters)
- Dynamic template selection based on runtime values
- Organizing related templates by category
For single templates, use `exe @func(params) = template "file.att"` instead.
**Basic node imports:**
```mlld
import { basename } from node @path
var @name = @basename("/tmp/file.txt")
show @name
```
**Namespace imports:**
```mlld
import { posix } from node @path
var @dir = @posix.dirname("/tmp/file.txt")
show @dir
```
**Constructor expressions:**
```mlld
import { URL } from node @url
exe @site = new @URL("https://example.com/path?x=1")
show @site.hostname
show @site.pathname
```
**Automatic wrapping:**
- Functions are wrapped to accept and return mlld values
- Method binding is preserved (correct `this` context)
- Classes support `new @Constructor()` syntax
- Async iterables are wrapped as streams
- Promises work transparently
- Callback-style functions trigger warnings
**Module resolution:**
- Uses Node.js module resolution from the importing file's directory
- Supports both CommonJS and ES modules
- Built-in modules: `node @path`, `node @fs`, `node @url`, etc.
- NPM packages: Use package name without leading `@` (e.g., `node @chalk`)
| Type | Behavior | Use Case |
|------|----------|----------|
| `module` | Content-addressed cache | Registry modules (default) |
| `static` | Embedded at parse time | Prompts, templates |
| `live` | Always fresh | Status APIs |
| `cached(TTL)` | Time-based cache | Feeds, configs |
| `local` | Dev modules (llm/modules/) | Development |
| `templates` | Directory of .att files | Template collections |
```mlld
import module { @api } from @corp/tools
import static { @prompt } from "./prompt.md"
import live as @status
import cached(1h) as @feed
import local { @dev } from @alice/experimental
```
**Built-in:**
- `@author/module` → Registry
- `@root/file` → Project root (preferred)
- `@base/file` → Project root (alias for @root)
- `./file.mld` → Local (with fuzzy extension matching)
**Custom prefixes** (mlld-config.json):
```json
{
"resolvers": {
"prefixes": [
{
"prefix": "@lib/",
"resolver": "LOCAL",
"config": { "basePath": "./src/lib" }
},
{
"prefix": "@company/",
"resolver": "GITHUB",
"config": {
"repository": "company/private-modules",
"branch": "main"
}
}
]
}
}
```
`mlld` reads `resolvers.prefixes` for resolver mappings. It also reads top-level `resolverPrefixes` when present, and CLI writers persist the nested `resolvers.prefixes` shape.
**Quick setup:**
```bash
mlld alias --name notes --path ~/notes
mlld alias --name shared --path ../shared --global
mlld setup --github # private repo wizard
```
When you install modules, mlld creates `mlld-lock.json` to ensure reproducible imports. This file tracks exact versions and content hashes.
```json
{
"lockfileVersion": 1,
"modules": {
"@alice/utils": {
"version": "1.0.0",
"resolved": "abc123def456...",
"source": "@alice/utils",
"sourceUrl": "https://registry.mlld.org/modules/@alice/utils/1.0.0",
"integrity": "sha256:abc123...",
"fetchedAt": "2024-01-15T10:00:00Z",
"registryVersion": "1.0.0"
}
}
}
```
**Lock entry fields:**
- **version** - The exact version installed (from registry version.json)
- **resolved** - Content hash used for cache lookup (SHA256)
- **source** - Original module specifier from your imports
- **sourceUrl** - URL where the module was fetched from
- **integrity** - Content hash for verification (sha256:...)
- **fetchedAt** - Timestamp when module was installed
- **registryVersion** - Version from registry metadata (only for registry modules)
**Behavior:**
- **Auto-generated** - Created/updated by `mlld install`
- **Version control** - Commit to git for reproducible builds
- **Never edit manually** - Use CLI commands to update
- **Registry-only validation** - Lock file only enforces version matches for registry modules
- **Version pinning** - `version: "latest"` updates to newest on `mlld update`; exact version (e.g. `"1.2.0"`) stays pinned until manually changed
Module-first design: keep `.mld` files readable; move complexity into focused modules. Avoid "kitchen sink" modules or side effects on import.
```mlld
---
name: text-utils
author: alice
version: 1.0.0
about: String helpers
license: CC0
---
needs {
js: []
}
exe @upper(s) = js { return s.toUpperCase() }
exe @trim(s) = js { return s.trim() }
export { @upper, @trim }
```
**Frontmatter fields:**
- `name` - Module name (required for registry)
- `author` - Your username (required for registry)
- `version` - Semver version
- `about` - Brief description
- `license` - License (CC0 recommended)
Modules only expose items listed in `export { }`. Unexported items stay private.
```mlld
exe @greet(name) = `Hello, @name!`
exe @farewell(name) = `Goodbye, @name!`
var @_helper = "internal"
>> Only @greet and @farewell are visible to importers
export { @greet, @farewell }
```
**Why explicit exports:** Encapsulation. Importers see a clean API surface. Internal helpers, intermediate variables, and implementation details stay hidden. Rename or remove internals without breaking callers.
**Wildcard export:** Export everything (same as no `export` directive):
```mlld
export { * }
```
**Environment module pattern:** Modules that wrap credentials export executables and let callers import the policy separately for credential configuration:
```mlld
policy @p = {
auth: {
claude: { from: "keychain:mlld-box-{projectname}/claude-dev", as: "ANTHROPIC_API_KEY" }
}
}
exe @spawn(prompt) = run cmd { claude -p "@prompt" } using auth:claude
export { @spawn }
```
**Notes:**
- Accessing unexported items via namespace import raises a runtime error
- Exported executables do not expose captured module internals through field access
- Guards can be exported alongside variables
- Without an `export` directive, all module-level items are auto-exported
Modules are directories with an entry point, manifest, and optional supporting files.
```
mymodule/
├── index.mld # Entry point
├── module.yml # Manifest (or .yaml, .json)
├── README.md # Documentation
└── lib/ # Optional supporting files
```
module.yml format:
```yaml
name: myapp
author: alice
type: app # library | app | command | skill
about: "Description"
version: 1.0.0
license: CC0
```
Directory modules use two metadata sources:
| Source | Lives In | Used For |
|--------|----------|----------|
| Frontmatter | Entry `.mld` file (`--- ... ---`) | Runtime metadata (`@fm`, imported namespace `.__meta__`) |
| `module.yml` | Module directory root | Packaging metadata (`type`, publish/install metadata) |
Keep shared identity fields (`name`, `author`, `version`, `about`) aligned between frontmatter and `module.yml`.
| Type | Purpose | Local Path | Global Path |
|------|---------|------------|-------------|
| library | Importable code | llm/lib/{name}/ | ~/.mlld/lib/{name}/ |
| app | Runnable scripts | llm/run/{name}/ | ~/.mlld/run/{name}/ |
| command | Claude slash cmd | .claude/commands/{name}/ | ~/.claude/commands/{name}/ |
| skill | Claude skill | .claude/skills/{name}/ | ~/.claude/skills/{name}/ |
```bash
mlld module app myapp # Create app in llm/run/myapp/
mlld module library utils # Create library in llm/lib/utils/
mlld module command review # Create command in .claude/commands/review/
mlld module skill helper # Create skill in .claude/skills/helper/
mlld module app myapp --global # Create in ~/.mlld/run/myapp/
```
```bash
mlld run myapp # Runs llm/run/myapp/index.mld
mlld run # Lists available scripts including apps
```
Use `index.mld` as the module entry point convention.
```bash
mlld install @author/my-app --global # Install to ~/.mlld/run/my-app/
mlld install @author/my-lib -g # Install to ~/.mlld/lib/my-lib/
```
Packed modules are single-file bundles created by `mlld pack` (future feature).
Use packed format for gist publishing; standard module format otherwise.
**Module patterns:**
```mlld
>> Library module (wraps a core exe with shortcuts)
import { @claude } from @mlld/claude
exe llm @haiku(prompt) = @claude(@prompt, { model: "haiku" })
exe llm @sonnet(prompt) = @claude(@prompt, { model: "sonnet" })
export { @haiku, @sonnet }
>> Config/agent module
var @meta = { id: @fm.id, name: @fm.name }
var @prompts = { primary: @primaryPrompt, optional: @optionalPrompt }
export { @meta, @prompts }
>> Gate module
exe @gate(response, instruction, message) = [...]
export { @gate }
```
```
llm/modules/
├── my-utils.mld.md # author: alice, name: experimental
└── helpers.mld # author: bob, name: tools
```
```mlld
import local { @helper } from @alice/experimental
```
Matched by frontmatter `author` and `name` fields.
## Prerequisites
1. **GitHub account** - You'll authenticate via GitHub
2. **mlld CLI** - Install via `npm install -g mlld`
3. **Module file** - Your `.mld`/`.mld.md` file with frontmatter and `needs`
## Required Metadata
```yaml
---
name: my-tool # or use title: My Tool (name falls back from title)
author: yourname
version: 1.0.0
about: Brief description # description: ... also supported
tags: [utils, strings] # optional (keywords also supported)
license: CC0
---
```
Required: module name/title, author, version, and about/description. License must be CC0.
`mlld publish` now reads these frontmatter fields as metadata defaults, and CLI metadata flags override them when provided.
## Authentication
```bash
mlld auth login # Opens GitHub OAuth flow
mlld auth status # Check auth status
mlld auth logout # Logout
```
Grants `gist` scope (create Gists for module source) and `public_repo` scope (create PRs to registry).
## First-Time Module (PR Workflow)
```bash
mlld publish my-tool.mld.md
```
1. **Validation** - Checks syntax, exports, metadata
2. **Source Creation** - Creates Gist or references repo
3. **PR Creation** - Opens PR to mlld-lang/registry
4. **Automated Review** - LLM reviews for no hardcoded secrets, safe operations, real utility, proper licensing
5. **Manual Review** (if needed) - Maintainer approval
6. **Merge** - Module becomes available
7. **Publish Rights** - You can update directly going forward
## Module Updates (Direct Publish)
After first module is merged:
```bash
mlld publish my-tool.mld.md
```
1. **Version Bump** - Prompts for patch/minor/major
2. **Validation** - Same checks as first-time
3. **Direct Publish** - No PR needed (if authenticated)
4. **Registry Update** - New version available immediately
Force PR workflow: `mlld publish --pr my-tool.mld.md`
## Module Source
**GitHub Repository** — If your module is in a git repo, `mlld publish` detects the repository URL, current commit SHA, module file path, and whether repo is clean. Source references the commit SHA, ensuring immutability.
**Gist** — If not in a repo, creates a GitHub Gist automatically with module content, versioned via Gist revisions.
## Validation
Your module must pass validation before publishing:
- **Syntax** - No syntax errors, no reserved word conflicts, valid directives
- **Exports** - `export` directive present, exported names exist, no duplicates
- **Imports** - Valid module references, no circular dependencies
- **Metadata** - All required fields present, author matches GitHub username, license is CC0, version follows semver
## Automated Review
LLM reviews check for no secrets, safe operations, real utility, proper licensing, accurate metadata. Review posts as PR comment with APPROVE, REQUEST_CHANGES, or COMMENT.
Push new commits to trigger re-review. Trusted authors in allowlist skip LLM review and auto-merge if CI passes.
## Direct Publish via API
```bash
mlld publish my-tool.mld.md # Via CLI (recommended)
curl -X POST https://registry-api.mlld.org/api/publish \
-H "Authorization: Bearer $TOKEN" \
-F "module=@my-tool.mld.md" # Via API directly
```
## Commands
```bash
mlld publish my-tool.mld.md # Publish to registry
mlld publish --pr my-tool.mld.md # Force PR workflow
mlld publish --tag beta my-tool.mld.md # Publish with tag
mlld publish --dry-run my-tool.mld.md # Validate without publishing
mlld publish my-tool.mld.md --title "My Tool" --tags utils,strings
```
For private or internal modules, use local imports or custom resolvers instead of the public registry.
## Local Filesystem
Distribute modules via git or file sharing:
```mlld
import { @helper } from "./shared/utils.mld"
import "./lib/internal" as @internal
```
## Custom Resolvers
Configure custom `@` prefixes for private registries or internal repos:
```json
{
"resolvers": {
"@company": "https://internal-registry.company.com/modules"
}
}
```
```mlld
import { @auth } from @company/auth-utils
```
See `mlld howto resolvers` for resolver configuration.
## Development Modules
Use `llm/modules/` for in-development modules:
```mlld
import local { @tool } from @alice/dev-module
```
These are resolved from the local `llm/modules/` directory without registry lookup.
**Check installed modules:**
```bash
mlld ls # list all installed modules with versions
```
**Update a specific module:**
```bash
mlld update @alice/utils # fetch latest version
```
**Update all modules:**
```bash
mlld update # updates everything in mlld-lock.json
```
**Check module info:**
```bash
mlld registry info @alice/utils # show module details from registry
```
**Version pinning:**
The lock file (`mlld-lock.json`) tracks installed versions:
- `version: "latest"` - updates to newest on `mlld update`
- `version: "1.2.0"` - stays pinned until manually changed
**After updating:**
Verify new exports are available:
```bash
mlld validate your-file.mld # check imports resolve
```
## Semantic Versioning
Follow semver (major.minor.patch):
- **1.0.0** - Initial release
- **1.0.1** - Bug fix (backward compatible)
- **1.1.0** - New feature (backward compatible)
- **2.0.0** - Breaking change
## Version Tags
Publish with tags:
```bash
mlld publish --tag beta my-tool.mld.md
```
Import via tag:
```mlld
import { @helper } from @alice/my-tool@beta
import { @helper } from @alice/utils@^1.0.0
```
Common tags:
- `latest` - Most recent stable (default)
- `stable` - Recommended version
- `beta` - Beta testing
- `alpha` - Alpha testing
## Version Ranges
Specify ranges in `mlld-config.json`:
```json
{
"dependencies": {
"@alice/my-tool": "^1.0.0",
"@bob/utils": "~1.2.0",
"@eve/lib": ">=1.0.0 <2.0.0"
}
}
```
Lock file pins exact versions.
## Version Resolution in Imports
| Import | Version |
|--------|---------|
| `import ... from @alice/utils` | latest |
| `import ... from @alice/utils@1.0.0` | exact |
| `import ... from @alice/utils@^1.0.0` | compatible |
| `import ... from @alice/utils@beta` | tag |
## Registry Structure
Modules are stored with version history:
```
registry/
└── modules/
└── alice/
└── my-tool/
├── metadata.json # Core info, owners
├── 1.0.0.json # Version 1.0.0
├── 1.0.1.json # Version 1.0.1
└── tags.json # latest, stable, etc.
```
## metadata.json
```json
{
"name": "my-tool",
"author": "alice",
"about": "Brief description",
"owners": ["alice"],
"maintainers": [],
"created": "2024-01-01T00:00:00Z",
"createdBy": 12345,
"firstPublishPR": 123
}
```
## {version}.json
```json
{
"version": "1.0.0",
"needs": ["js", "sh"],
"license": "CC0",
"mlldVersion": ">=1.0.0",
"source": {
"type": "github",
"url": "https://raw.githubusercontent.com/...",
"contentHash": "sha256:abc123...",
"repository": {
"type": "git",
"url": "https://github.com/alice/repo",
"commit": "abc123",
"path": "my-tool.mld.md"
}
},
"dependencies": {
"js": {
"packages": ["lodash"]
}
},
"keywords": ["utility", "automation"],
"publishedAt": "2024-01-01T00:00:00Z",
"publishedBy": 12345
}
```
Key fields:
- **publishedBy** - GitHub user ID of the publisher (numeric ID, not username)
- **publishedAt** - ISO timestamp when this version was published
- **source.type** - Source type: `github`, `gist`, or `private-repo`
- **source.contentHash** - SHA256 hash for content verification
- **source.repository** - Git repository metadata (for github/private-repo sources)
## tags.json
```json
{
"latest": "1.0.1",
"stable": "1.0.1",
"beta": "2.0.0-beta.1"
}
```
## Registry API
```bash
# Resolve version
curl https://registry-api.mlld.org/api/resolve?module=@alice/my-tool
# Direct publish
curl -X POST https://registry-api.mlld.org/api/publish \
-H "Authorization: Bearer $TOKEN" \
-F "module=@my-tool.mld.md"
```
## Module Owners
After your first PR merges, you become module owner:
- Can publish updates directly (no PR needed)
- Can add maintainers
- Module namespaced under your GitHub username
## Maintainers
Add collaborators to `metadata.json`:
```json
{
"owners": ["alice"],
"maintainers": ["bob", "eve"]
}
```
Maintainers can also publish updates.
## Organization Modules
Publish under org namespace:
```yaml
---
author: company
name: auth-tool
---
```
Requires write access to `@company` in registry.
LLM modules follow a `(prompt, config)` calling convention. The first argument is always the prompt text. The second is an optional config object that controls model selection, tool access, streaming, and other behavior.
```mlld
import { @claude, @haiku } from @mlld/claude
>> Simple — model shortcut, no config
show @haiku("What is REST?")
>> Full control — config object
var @result = @claude("Review the auth module", {
model: "opus",
tools: ["Read", "Grep"],
stream: true
})
```
## The config object
The `@mlld/claude` module defines these config fields:
| Field | Type | Default | Purpose |
|-------|------|---------|---------|
| `model` | string | `"sonnet"` | Model name: `haiku`, `sonnet`, `opus` |
| `dir` | string | `@root` | Working directory for tool operations |
| `tools` | array | — | Tool access list (see below) |
| `stream` | boolean | — | Enable token streaming |
| `system` | string | — | Appended system prompt |
Poll variants (`@claudePoll`, `@claudePollJsonl`, `@claudePollEvent`) extend the config with additional fields like `poll`, `timeout`, `pattern`, `event`, and `itemId`. See the `@mlld/claude` module README for details.
**Convention, not schema:** The runtime recognizes `tools` and `dir` from the config object (see next section). Everything else — `model`, `system`, `stream` — is handled by the module implementation, not the runtime. Module authors can add their own fields.
## Runtime support for `config.tools`
When an `exe llm` function is invoked with a config object containing a `tools` property, the runtime automatically:
1. **Detects** the second argument as a config object (plain object with a `tools` key)
2. **Normalizes** the tools array (strings for built-in tools, exe refs for mlld functions)
3. **Reads `config.dir`** if present, to set the working directory for bridge operations
4. **Creates MCP bridges** — temporary servers that expose the requested tools
5. **Populates `@mx.llm`** with bridge metadata for the exe body to consume
```mlld
exe llm @agent(prompt, config) = [
>> These are set automatically by the runtime:
>> @mx.llm.config — path to generated MCP config file (empty string if no bridges)
>> @mx.llm.allowed — unified tool name list for --allowedTools
>> @mx.llm.native — native tool names CSV (empty when no native tools requested)
>> @mx.llm.inBox — true when inside a box with active VFS bridge
>> @mx.llm.hasTools — true when config.tools was specified
=> @prompt | cmd { claude -p --model sonnet --allowedTools "@mx.llm.allowed" }
]
```
This is why `exe llm` functions don't need to manually construct `--mcp-config` flags or manage bridge lifecycles — the runtime handles it.
### Tool types in the array
The `tools` array accepts a mix of string names and exe references:
```mlld
exe @summarize(text) = cmd { ... }
var @r = @claude("Analyze this codebase", {
tools: ["Read", "Grep", @summarize]
})
```
| Entry type | What happens |
|------------|-------------|
| String (`"Read"`) | Routed to built-in capability. Inside a box, proxied through VFS bridge. Outside a box, passed as native CLI tool name. |
| Exe ref (`@summarize`) | Wrapped as an MCP tool via a function bridge. The runtime generates a JSON schema from the function signature. |
### Empty tools
Passing an empty array signals "no tools" — the runtime sets `@mx.llm.hasTools = true` but provides no bridges. The module can use this to pass `--tools ""` to disable all default tools:
```mlld
var @r = @claude("Pure text generation", { tools: [] })
```
## Box interaction
Tool routing changes based on whether the call happens inside a `box`:
**Outside a box:** String tools pass through as native names. The child process uses them directly.
**Inside a box:** String tools are proxied through the workspace VFS bridge. File operations route through the box's in-memory filesystem, and the child process cannot access the real filesystem.
```mlld
var @ws = box [
file "data.csv" = "id,name\n1,Alice\n2,Bob"
let @r = @claude("Summarize the CSV file", {
model: "haiku",
tools: ["Read"]
})
>> @claude's Read tool sees data.csv in the VFS, not the real filesystem
=> @r
]
```
The `@mx.llm.native` field tells module implementations which native tools are active. When `native` is empty — either because the call is inside a box (all tools route through VFS bridges) or because the tools array contains only exe refs — the module should pass `--tools ""` to suppress the CLI's default built-in tools. The `@mlld/claude` module does this automatically.
## Streaming
Streaming is wired at the exe definition level using `with { stream, streamFormat }`:
```mlld
exe llm @myLLM(prompt, config) = [
let @cfg = @config ? @config : {}
=> @prompt | cmd { claude -p --model sonnet }
] with { stream: @cfg.stream, streamFormat: @myAdapter }
```
The `@mlld/claude` module does this internally — when you pass `stream: true` in config, the underlying exe's `with` clause activates streaming. You don't need to configure this when using the module:
```mlld
show @claude("Explain TCP/IP", { model: "haiku", stream: true })
```
See `stream` for format adapters and streaming details.
## Writing your own LLM module
An LLM module follows this pattern:
```mlld
>> 1. Label with llm so the runtime processes config.tools
>> 2. Accept (prompt, config) as the calling convention
>> 3. Read @mx.llm for bridge metadata set by the runtime
exe llm @myLLM(prompt, config) = [
let @cfg = @config ? @config : {}
let @model = @cfg.model ? @cfg.model : "sonnet"
=> when [
@mx.llm && @mx.llm.config && @mx.llm.native => @prompt | cmd {
my-llm-cli --model @model --mcp-config "@mx.llm.config" --tools "@mx.llm.allowed"
}
@mx.llm && @mx.llm.config => @prompt | cmd {
my-llm-cli --model @model --disable-builtin-tools --mcp-config "@mx.llm.config" --tools "@mx.llm.allowed"
}
* => @prompt | cmd {
my-llm-cli --model @model
}
]
]
>> Model shortcuts
exe llm @fast(prompt) = @myLLM(@prompt, { model: "fast" })
exe llm @smart(prompt) = @myLLM(@prompt, { model: "smart" })
export { @myLLM, @fast, @smart }
```
**Key points:**
- The `llm` label is required — without it, the runtime won't process `config.tools` or populate `@mx.llm`
- Always default missing config: `let @cfg = @config ? @config : {}`
- Branch on `@mx.llm.config` and `@mx.llm.native` to handle bridged, exe-ref-only, and unbridged invocations
- Shortcuts should delegate to the core exe, not duplicate the implementation
- The runtime cleans up bridge temp files automatically when the exe scope exits