Skip to content
Data
Skill

/notebook

Load before you edit or run notebook cells in chat. Covers the notebook tools, the sp SDK, governed queries, files, and charts.

BOOST
From plugin
signalpilot
48728 skills2 agents
Install
$ npx -y skills add signalpilot-labs/signalpilot --skill notebook --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/notebook

Context preview

The summary Claude sees to decide when to auto-load this skill.

Load before you edit or run notebook cells in chat. Covers the notebook tools, the sp SDK, governed queries, files, and charts.

SKILL.md

notebook.SKILL.md
name: notebook
description: "Load before you edit or run notebook cells in chat. Covers the notebook tools, the sp SDK, governed queries, files, and charts."
type: skill

Notebook

The chat gives you one analysis notebook and optional named notebooks. Use them to run queries, compute results, and save files. Evidence for an answer comes from the analysis notebook.

Start a notebook

1. Call `start_analysis_notebook` only when you will run cells. A notebook that you start and do not use rejects the whole run. 2. Pass `notebook: "scratch"` (short, lowercase) to start a separate notebook for drafting. Each name has its own kernel and `session_id`. 3. Use the `session_id` from the tool result with every notebook tool. 4. Make the first visible cell a title: `sp.md("# <title of the analysis>")`. Write a short title from the user's request, not the prompt text. Update it when the question changes. A markdown cell does not need `sp.init()` and must not import `signalpilot` again.

The seeded cells

The notebook starts with two hidden cells and one empty visible cell. Never edit or remove the hidden cells. Put the title in the empty cell.

  • Cell 1 DEFINES `os`, `Path`, `runtime_context` and `sp`, sets

`SP_CHAT_SCRATCH_DIRECTORY` and `SP_CHAT_ARTIFACTS_DIRECTORY`, and EXPORTS `Path`, `runtime_context`, `sp`.

  • Cell 2 defines and exports `db`, `np`, `pd`.

Redefining any of those seven names raises `MultipleDefinitionError`. That includes `import os`, even though `os` is not exported to your cells: it is defined, so you cannot define it again, and it is not exported, so you cannot use it. Import nothing that is already defined.

**Nothing else is defined. `plt` is not.** `sp.init()` has already applied the house theme to matplotlib's `rcParams`, including `savefig.dpi` and `savefig.bbox`, but the module itself is not imported. Put `import matplotlib.pyplot as plt` in one cell of your own before the first chart cell.

`sp.init()` returns None. There is no `signalpilot.db` export. Use `db` and `sp` from these cells. Do not import `signalpilot` again.

Before you write the first cell

Write every cell of the analysis before running any of them. Fixing cells until they stop erroring produces code that runs and answers the wrong question.

Settle these four facts first, from the dbt project and `sp.describe_table`, not by trial:

1. The connection's SQL dialect. SQL Server takes `YEAR(x)`, `select top 10`, and three-part `Database.schema.table` names, which the governed parser requires. 2. The definition of every column you will name, read from its model SQL. A column that sums cleanly can still mean something other than its name. 3. Which names must be public because a later cell reads them, and which are `_`-prefixed scratch private to one cell. 4. The grain of each mart, so you know whether your `GROUP BY` fans out.

Cell rules (marimo, not Jupyter)

  • Put one text cell before every code cell. The text cell gives a one line

summary of what the code cell below it does. Give every code cell its own summary, including the first one.

  • In marimo a text cell is a separate cell. It holds one `sp.md("...")` call.

Each step is therefore two cells: the summary cell, then the code cell.

  • Add both cells in the same `edit_notebook` batch. Add the summary cell

first, so the notebook reads in the correct order.

  • Write what the step does, in one line. Write

`sp.md("Join invoice lines to the account dimension for one row per account per month")`. Do not write `sp.md("Run a query")`.

  • Use one line. Do not use a heading mark.
  • A text cell does not need `sp.init()`. Do not import `signalpilot` in it. A

text cell defines no name, so it cannot cause `MultipleDefinitionError`.

  • One live cell defines each top-level name. A second definition raises

`MultipleDefinitionError`. Fix all conflicting cells in one edit batch.

  • An underscore name is local to the cell that writes it, and the editor

rejects the WHOLE batch if another cell reads it. Decide before you write: a value the next cell needs gets a public name; a value used only inside this cell gets one underscore. `_fig`, `_ax` and loop variables are always underscore. After a `PrivateVariableCrossCellReference`, promote the name in the defining cell and resend both cells in one batch.

  • Get every DataFrame with `db.query_df(sql)`. Show a small preview only.
  • One query per cell, and no more than a few derived columns after it.

Aggregate in SQL; use Python for ratios and formatting only. Keep a cell under 20 lines, define at most one public name in it, and end it in one bare expression or one `print`. A cell that runs three queries and twenty pandas statements fails as a unit, and its error output is truncated.

  • Batching many small cells into one `edit_notebook` call is correct. Putting

many steps into one cell is not.

  • `pd` and `np` are already imported in the setup cell. Do not import them

again.

  • Use only the methods this skill names. The SDK has no other functions.

Tools

| Tool | Use | |---|---| | `edit_notebook` | `edits`: `add_cell` (code), `update_cell` (cell_id, code), `delete_cell` (cell_id). One batch per change set. | | `run_cells` | Runs the given `cell_ids` (all cells when omitted) and blocks. Returns outputs, console text, and errors per cell. Set `timeout` for long queries. | | `save_data_snapshot` | Saves a compact aggregate for an external deliverable. Do not dump raw tables. | | `start_notebook_session` | Opens a `.py` notebook file you wrote with `Write` and returns its `session_id`. |

Read every `run_cells` result. A cell with `status: "failed"` has an `error.message`. Fix the cell and run it again.

The sp SDK

| Call | Result | |---|---| | `sp.connections()` | Names of the connections you may query. | | `db.query_df(sql, row_limit=1000)` | **The default.** A typed DataFrame: numeric and date columns are already converted.

Read more
Ships withsignalpilot
Get the whole plugin
Stats
487
Stars
27
Forks
Active
Maintenance
Python
Language
Apache-2.0
License
17d ago
Last commit
6mo ago
Created
4h ago
Added

Repo: signalpilot-labs/signalpilot

Other skills on signalpilot.