skills/marimo-notebook/SKILL.md
Write and edit marimo reactive Python notebooks in the correct file format with proper cell structure, setup cells, reactivity patterns, and Typer CLI argument support. Use when the user asks to "create a marimo notebook", "edit a marimo file", "add marimo cells", "make a marimo script", "add CLI arguments to a marimo notebook", or when editing a Python file that imports marimo. Do NOT use for general Python scripting without marimo, for Jupyter notebooks, or for explaining marimo concepts without making code changes.
npx skillsauth add armandli/get-skilled marimo-notebookInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
# Run as script (non-interactive, for testing)
uv run <notebook.py>
# Run interactively in browser
uv run marimo run <notebook.py>
# Edit interactively
uv run marimo edit <notebook.py>
Every marimo notebook MUST have a with app.setup: block immediately after app = marimo.App(...). Always import marimo as mo and typer in the setup cell:
app = marimo.App(width="medium")
with app.setup:
import marimo as mo
import typer
Add any other shared imports (numpy, polars, torch, etc.) to the setup cell as well.
Use mo.app_meta().mode == "script" in a dedicated cell:
@app.cell
def _(mo):
is_script_mode = mo.app_meta().mode == "script"
return (is_script_mode,)
Always create and show widgets. Only change the data source in script mode.
if not is_script_mode conditionals# Always show the widget
@app.cell
def _(mo):
scatter_widget = mo.ui.anywidget(ScatterWidget())
scatter_widget
return (scatter_widget,)
# Only change data source based on mode
@app.cell
def _(is_script_mode, scatter_widget):
if is_script_mode:
X, y = make_moons(n_samples=200, noise=0.2)
else:
X, y = scatter_widget.widget.data_as_X_y
return X, y
# Always show sliders - use .value in both modes
@app.cell
def _(mo):
lr_slider = mo.ui.slider(start=0.001, stop=0.1, value=0.01)
lr_slider
return (lr_slider,)
if StatementsMarimo's reactivity means cells only run when their dependencies are ready:
# BAD - the if statement prevents the chart from showing
@app.cell
def _(plt, training_results):
if training_results: # WRONG
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
# GOOD - let marimo handle the dependency
@app.cell
def _(plt, training_results):
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
# BAD - hiding errors behind try/except
@app.cell
def _(scatter_widget):
try:
X, y = scatter_widget.widget.data_as_X_y
except Exception as e:
return None, None
# GOOD - let it fail if something is wrong
@app.cell
def _(scatter_widget):
X, y = scatter_widget.widget.data_as_X_y
return X, y
Only use try/except for specific, expected exceptions with meaningful recovery.
Marimo only renders the final expression of a cell:
# BAD - indented expression won't render
@app.cell
def _(mo, condition):
if condition:
mo.md("This won't show!") # WRONG - indented
return
# GOOD - final expression renders
@app.cell
def _(mo, condition):
result = mo.md("Shown!") if condition else mo.md("Also shown!")
result
return
Use underscore prefix for loop variables to make them cell-private:
for _name, _model in items:
...
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "marimo",
# "typer",
# "torch>=2.0.0",
# ]
# ///
Always add typer when using CLI arguments.
# GOOD
from pathlib import Path
data_dir = Path(tempfile.mkdtemp())
parquet_file = data_dir / "data.parquet"
# BAD
import os
parquet_file = os.path.join(temp_dir, "data.parquet")
Run before delivering any notebook:
uvx marimo check <notebook.py>
uv --with marimo run python -c "import marimo as mo; help(mo.ui.form)"
Notebooks can accept --flags when run as uv run notebook.py using Typer. Use a module-level _cli_args dict to pass values from the CLI into cells. See references/TYPER.md for the full integration pattern and template.
Cells whose function names start with test_ are discovered as pytest tests. Cell arguments inject other cells' return values as test inputs:
pytest notebook.py # run all tests
pytest notebook.py -v # verbose
See references/PYTEST.md for fixtures, parametrize, and class-based test patterns.
Import polars as pl in the setup cell. Marimo renders DataFrames natively — drop a DataFrame as the final cell expression, or use:
mo.ui.table(df) — interactive table with row selectionmo.ui.dataframe(df) — no-code filter/groupby GUImo.ui.data_explorer(df) — column statistics and chart builderSee references/POLARS.md for the full API.
Import altair as alt in the setup cell. Wrap charts with mo.ui.altair_chart(chart) for reactivity — .value returns selected data as a DataFrame. See references/ALTAIR.md for chart types and composition.
Import numpy as np in the setup cell. Use for numerical computation, array math, and linear algebra. See references/NUMPY.md for the full API.
Import from scipy submodules in the setup cell (e.g., from scipy import stats, optimize). Use for statistics, optimization, signal processing, and integration. See references/SCIPY.md for the full API.
Import torch and torch.nn in the setup cell. Define models with nn.Module, train with autograd. See references/PYTORCH.md for the full API.
After the skill's primary task completes, run:
python3 ${PWD}/.claude/skills/skill-stat/scripts/record-stat.py "marimo-notebook"
tools
--- name: update-readme description: Updates a project README.md with build instructions, unit test instructions, and a mermaid architecture diagram. Use when a project README needs to be created or refreshed. Trigger phrases: "update readme", "generate readme", "create readme", "refresh readme docs". Emphasizes project interfaces, extension points, and customization hooks in the diagram — not concrete implementations. Do NOT use for documentation sites, wikis, or non-project READMEs. argument-h
business
--- name: skill-stat description: Records skill usage statistics and issue reports into .claude/skill-stats.md. Increments the Uses count for a skill name, and optionally logs an issue report that increments the Issues count and appends a row to the Issue Reports table. Use when tracking how often a skill is invoked, when a user reports a problem with a skill, or when another skill needs to log its own usage. Trigger phrases: "record skill stat", "log skill usage", "report skill issue". Do NOT u
testing
--- name: revert description: Reverts ALL git changes in the working directory: staged changes, unstaged modifications, and new untracked files. Use when user asks to "revert all changes", "undo all changes", "discard all changes", "reset all git changes", or "clean working directory". Do NOT use for reverting a specific file or a specific commit — those need targeted git commands. disable-model-invocation: true --- Revert all git changes in the working directory. This is destructive and cannot
tools
Scans a Python codebase for duplicate or near-duplicate logic patterns across functions, classes, and files, then extracts those patterns into typed utility classes in a shared module. Use when the user asks to "refactor this Python code", "find duplicate logic", "extract shared utilities", "apply DRY to Python", "deduplicate Python code", or "find repeated patterns in Python". Groups extracted helpers by the object type they operate on (strings, numbers, dates, collections, etc.). Do NOT use for performance optimization (use optimize-python), for debugging logic errors, or for explaining code. Do NOT extract code that appears only once. Run this skill before optimize-python.