From aqora-skills
Work inside a running aqora-hosted marimo workspace. List workspaces, execute code in the live kernel, create and edit cells. Use this skill whenever the user mentions an aqora workspace, aqora.io, or a marimo notebook on aqora, or wants to build, explore, debug, or modify anything in an aqora-hosted notebook, even if they do not explicitly say "workspace".
How this skill is triggered — by the user, by Claude, or both
Slash command
/aqora-skills:aqora-workspaceThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill gives you full access to a running aqora workspace. Aqora workspaces are marimo notebooks hosted on aqora.io infrastructure. You can read cell code, create and edit cells, install packages, run cells, and inspect the reactive graph, all through the bundled scripts.
This skill gives you full access to a running aqora workspace. Aqora workspaces are marimo notebooks hosted on aqora.io infrastructure. You can read cell code, create and edit cells, install packages, run cells, and inspect the reactive graph, all through the bundled scripts.
Aqora workspaces are reactive notebooks. Cells are the fundamental unit of computation, connected by the variables they define and reference. When a cell runs, marimo re-executes downstream cells automatically. You have full access to the running kernel.
pyproject.toml, and existing cells before reaching for external tools.Aqora workspaces require authentication. Set up credentials one of two ways:
aqora login. The scripts read cached credentials automatically.AQORA_TOKEN environment variable to a personal access token.If neither is present, the scripts fail with a clear error. See references/auth.md for the full story.
list-workspaces.sh needs bash, curl, and jq on your PATH.execute-code.py needs Python 3.10+ plus httpx and websockets. If uv is installed (recommended), the script is fully self-contained thanks to PEP 723 inline metadata: just run it. Without uv, run pip install httpx websockets once.No marimo installation is needed locally: all code runs in the remote kernel.
Two core operations: list workspaces and execute code.
| Operation | Script |
|---|---|
| List workspaces | bash scripts/list-workspaces.sh |
| Execute code (inline) | scripts/execute-code.py --workspace <id> -c "code" |
| Execute code (multiline) | scripts/execute-code.py --workspace <id> <<'EOF' ... EOF |
| Execute code (from file) | scripts/execute-code.py --workspace <id> script.py |
| Execute code (by URL) | scripts/execute-code.py --url https://... -c "code" |
| Mutate cells and save | scripts/execute-code.py --workspace <id> --persist <<'EOF' ... EOF |
list-workspaces.sh returns JSON with workspaces owned by the current viewer. Each entry includes id, slug, name, shortDescription, plus two runner slots: editor_url / editor_command and viewer_url / viewer_command. A non-null *_url means a kernel is live for that mode. Both null means nothing is running and the workspace has to be started from the aqora UI first. See references/workspace-lifecycle.md.
Filter by id to get one workspace, or add --url-only when scripting:
bash scripts/list-workspaces.sh --id ws_abc --url-only
Every execute-code.py call runs inside the remote marimo kernel. All cell variables are in scope. print(df.head()) just works. Nothing you define persists between calls, but you can freely introspect notebook state: inspect variables, test snippets, check types and shapes. Use this to explore and validate before committing anything to the notebook, then create cells to persist state and make results visible to the user.
Under the hood, execute-code.py opens a short-lived WebSocket to register a session with the runner, POSTs your code to the agent-only /api/kernel/execute endpoint, streams results back, and closes. Stdout from the kernel is mirrored to your stdout, stderr to your stderr. All of this is transparent from the skill's point of view: treat the script as "run this code, get its output."
To mutate the notebook's dataflow graph, use marimo._code_mode and pass --persist to the script:
scripts/execute-code.py --workspace <id> --persist <<'EOF'
import marimo._code_mode as cm
async with cm.get_context() as ctx:
cid = ctx.create_cell("x = 1")
ctx.packages.add("pandas")
ctx.run_cell(cid)
EOF
You must use async with. Without it, operations silently do nothing. All ctx.* methods are synchronous. They queue operations that flush on context exit. Do not await them.
The kernel supports top-level await, so use async with at the top level. Do not wrap calls in async def main(): ... with asyncio.run(). It is unnecessary and easy to get wrong (async with cannot follow def name(): on the same line, so a -c one-liner produces a SyntaxError).
Cells are not auto-executed. create_cell and edit_cell are structural changes only. Call run_cell to queue execution.
code_mode is the tested, safe API for notebook mutations. Prefer it for all structural changes. You have access to deeper marimo internals from the kernel, but treat that as a last resort.
Always pass --persist with mutation calls. code_mode updates the kernel's in-memory graph but does not touch the workspace's .py file. Without --persist, the browser editor shows stale (often empty) cell editors even though outputs render, because the editor reads cell code from the file. The flag appends a small epilogue that regenerates the file from kernel state. See references/persisting-cells.md. Skip --persist for pure introspection or one-off execution, since there is nothing to save.
The code_mode API can change between marimo versions. Explore it at the start of each session:
import marimo._code_mode as cm
help(cm)
Skip these and the workspace breaks.
ctx.packages.add(), not uv add or pip. The code API handles kernel restarts and dependency resolution correctly.mo.ui is fine for simple forms and controls. For bespoke visuals, use anywidget with HTML, CSS, and JS..py file directly while a session is running. The kernel owns it. The --persist flag on execute-code.py is the one sanctioned writer: it regenerates the file from the kernel's current cell graph, so the two stay in sync.pathlib.Path("/tmp/...") inside a cell is a bug. Temp state does not survive between kernel restarts.edit_cell into existing empty cells over creating new ones. Clean up any cell that ends up empty after an edit.ctx.packages.add() mutates the workspace's dependency set. Confirm when it is not obvious from context.Anywidget state (traitlets) lives outside marimo's reactive graph. To hook a widget trait into the graph, pick one strategy per widget. Never mix them.
mo.state plus .observe() lets you bridge specific traits by hand. This is the default.mo.ui.anywidget() wraps all synced traits into one reactive .value. Convenient but coarser.execute-code.sh call goes to aqora.io. Batch related operations into a single call when possible.list-workspaces.sh. See references/workspace-lifecycle.md.execute-code.py call is a full WebSocket plus HTTP round-trip. The overhead is small per call but not zero. Group related work into a single call when possible.--persist is required after cell mutations and what the appended epilogue does.Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub aqora-io/skills --plugin aqora-skills