| name | use-belgie |
| description | Embed JavaScript/TypeScript in Python with belgie — Runtime, Script, Environment, Command, npm/JSR deps without Node on PATH, JSON bridging, sync/async context managers, and error-driven troubleshooting. Use when the user mentions belgie, embedded JS in Python, Deno runtime, npm packages from Python, JSR imports, TypeScript scripts, or Belgie* errors. |
| license | MIT |
| compatibility | Requires Python >=3.12,<3.15 |
| allowed-tools | Bash(uv *) |
| metadata | {"version":"1.1.0","author":"belgie"} |
Use Belgie
Belgie embeds a Deno-powered JavaScript/TypeScript runtime inside Python through Runtime, Script, Environment,
and Command. This skill covers adoption, extension, and troubleshooting using only belgie's public API.
When to Use This Skill
Invoke this skill when:
- Running JavaScript or TypeScript from Python without Node.js on
PATH
- Installing npm or JSR packages in an isolated
Environment
- Executing npm package binaries (vite, esbuild, etc.) through
Command
- Wiring sync or async
with Runtime() / async with Runtime() context managers
- Bridging JSON data between Python and JavaScript
- Diagnosing
BelgieRuntimeError, BelgieModuleError, or BelgieJavaScriptError failures
- Building MCP Apps with
BelgieExtension and belgie[mcp]
- Configuring
[tool.belgie.dependencies] and @belgie/mcp/vite in pyproject / vite.config.ts
- Code imports
belgie, Runtime, Script, Environment, or Command
Do not use this skill for:
- Generic React or web-server work without belgie as the embedding layer (use
use-gdansk for gdansk as an alternative)
- Inspecting belgie internals when the public API or emitted error already explains the task
Principles
- Always enter
Environment and Runtime with context managers before binding or calling.
- Prefer Deno-style inline imports (
npm:, jsr:, or URL) directly in scripts.
- Use
Environment when scripts need a frozen lockfile, custom cache/options, local file: packages, or commands.
- Keep the Python ↔ JavaScript boundary JSON-serializable; design APIs with dicts, lists, and primitives.
- Use inline patterns in skill references rather than inventing architecture.
Critical Rules
These rules are always enforced. Each links to Incorrect/Correct pairs.
- Enter
Environment and Runtime with with or async with before use.
- Call
install() on the entered environment before commands, local file: packages, or dependency-map imports.
- Bind and call runners inside the active runtime context.
- JS modules must export a callable (
export default function run(...) or export default () => ...).
- Use
Script.from_file() for disk scripts; ./ imports resolve from the script file's directory.
- Use
Runtime.from_folder(path) for inline scripts with ./ imports or when the runtime cwd must be a project
root.
- Pass only JSON-serializable values across the boundary.
- Positional args become JS positional args; keyword args become a final
options object.
- Import errors from
belgie.errors.
Runtime() for dependency-free inline scripts and direct npm: / jsr: / URL imports.
Runtime(env=env) for dependency aliases, local file: packages, frozen lockfiles, or custom cache/options.
Runtime.from_folder(path) for inline ./ imports, inline dependencies with a fixed cwd, or a fixed project root.
Runtime(env=env) + Command(...) for npm package binaries.
Quick-Start Patterns
Minimal inline script
from belgie import Runtime, Script
with Runtime() as run:
result = run(Script("export default (n) => n + 1"))(41)
Inline dependency script
from belgie import Runtime, Script
script = Script(
"""
import { join } from "jsr:@std/path@^1";
export default function run() {
return join.name;
}
"""
)
with Runtime() as run:
assert run(script)() == "join"
Async npm command
import asyncio
from belgie import Command, Environment, Runtime
async def main() -> None:
async with Environment({"vite": "^6"}) as env:
await env.install()
async with Runtime(env=env) as run:
await run(Command("vite"))("--version")
asyncio.run(main())
Integration Selection
Agent Workflow
- Classify the request: inline script / file script / environment deps / command / pyproject / MCP / debug.
- Install:
uv add belgie in the consumer project (belgie[mcp] for MCP, belgie[cli] for CLI).
- Choose constructor:
Runtime(), Runtime.from_folder(), or Runtime(env=env) — see
rules/runtime-selection.md.
- Inline deps: for script packages, prefer direct
import ... from "npm:..." or import ... from "jsr:...".
- Environment: use
Environment({...}) and install() for commands, local file: packages, dependency aliases,
or explicit lock/cache/options.
- Pyproject deps: when
[tool.belgie.dependencies] is present, run belgie lock and belgie install before MCP
widget development, production builds, or shared lockfile usage.
- Enter contexts: nest
Runtime inside an active Environment when env= is used.
- Bind and call:
runner = run(Script(...)) or run(Command(...)), then call with JSON-safe args.
- On failure: match the error text in references/troubleshooting.md.
- After fix: re-run inside active contexts with
install() when commands or explicit environments are involved.
Task Routing Table
Load only the most relevant reference first. Read additional references only if the task spans multiple areas.
Key Practices
- Use the public integration surface:
Runtime, Script, Environment, Command, RuntimeOptions,
EnvironmentOptions.
- Install with
uv add belgie (belgie[mcp] for MCP, belgie[cli] for CLI).
- Import script packages inline with
npm:, jsr:, or URL specifiers; do not put JavaScript dependencies in Python
[project.dependencies].
- Use
[tool.belgie.dependencies] for Belgie-managed JS packages (including @belgie/mcp as an npm or file: dep).
- Put MCP entries at direct
<name>/widget.tsx paths, pass a Path to @tool(widget=...), and run Vite alongside
Python in development. Use belgie run vite build with BelgieExtension(dev=False) in production.
- Export a callable from every JS module (
export default function run(...) or export default () => ...).
- Call
env.install() before commands, local file: package aliases, or explicit dependency-map imports.
- Use
Runtime.from_folder() for inline ./ imports or a fixed project cwd; Script.from_file() resolves
relatives from the script directory. from_folder() does not install packages.
- Pass
Command args as separate str values; belgie does not parse shell strings.
- Import exceptions from
belgie.errors.
Common Gotchas
Agents commonly make these mistakes with belgie:
- Calling
install() or run(...) outside an active Environment / Runtime context (must be entered).
- Running dependency-map or local
file: imports without env.install() (package dependencies).
- Using an
Environment dependency map when a direct npm: or jsr: script import is simpler.
- Expecting
Runtime.from_folder() to install packages or read pyproject.toml.
- Using
Runtime.from_folder() for Script.from_file() when only the script directory matters for ./ imports.
- Exporting non-callable values from JS modules (
callable run function, not callable).
- Passing shell command strings to
Command instead of separate argv (argument 0 must be str).
- Putting JavaScript dependencies in Python
[project.dependencies] instead of inline script imports, Environment,
or [tool.belgie.dependencies].
- Starting an MCP path widget before Vite in development, skipping the production build, or using a string widget name
without a manifest/base URL.
- Using
@tool(path=...) instead of @tool(widget=...) with a prebuilt manifest / base_url.
- Calling a bound runner after the runtime context exits (
closed).
- Passing non-JSON Python objects across the boundary (
Only JSON-serializable).
- Importing
BelgieRuntimeError from top-level belgie instead of belgie.errors.
- Treating successful
Command calls as returning output; they return None on success.
- Importing the Vite plugin from
@belgie/mcp (browser entry) instead of @belgie/mcp/vite.