nbskill

nbskill is notebook-aware tooling for agents and maintainers working in nbdev projects. It provides focused context, structured edits, safe execution, and review through a Python Pyskill.

Why it exists

In nbdev, the notebook is the source of truth. It holds the implementation, its explanation, examples, outputs, and metadata. Raw JSON edits are easy to misplace, and changes to generated Python are overwritten. nbskill makes notebook work deliberate and reviewable.

What it does

Use nbskill to find the relevant project, notebook, chapter, cell, or symbol; edit the right cells; run focused checks in project context; and review the resulting diff and style.

What it offers

Need Tooling
Find the right context context reads a project, notebook, chapter, cell id, or symbol without raw JSON noise.
Make focused edits edit_notebook preserves cell structure, validates source, and returns a structured diff.
Verify behavior exec_nb runs the notebook in project context, with a check-only mode for review loops.
Verify a change diff_nb, exec_nb, and doctor show the source change, executed checks, and diagnostics.
Review changes diff_nb and doctor focus on behavior changes and notebook hygiene.
Use the library directly The nbskill.skill Pyskill exposes the curated Python workflow for an installed project environment.

Parse notebook cells

Notebook text uses %%markdown, %%code, and --- as a compact cell format. parse_cells turns that text into notebook cells while preserving each cell’s type and source.

cell_text = "\n".join(["%%markdown", "## Demo", "---", "%%code", "value = 42"])
parsed_cells = parse_cells(cell_text)
[(cell.cell_type, cell_source(cell).splitlines()[0]) for cell in parsed_cells]
[('markdown', '## Demo'), ('code', 'value = 42')]

Read a notebook

Start with the smallest useful view. context resolves a project, notebook, chapter, cell, or symbol without exposing notebook JSON. In Python, the first call explains the notebook and the second follows the public write_nb symbol to its owning cell and related uses.

with redirect_stdout(StringIO()): notebook_context = context("02_write.ipynb", mode="overview")
print("\n".join([x['source'] for x in notebook_context["selection"]["markdown"]]))

Python workflow

Load the curated Python workflow through Pyskills:

from nbskill.skill import *

context("nbs/02_write.ipynb#write_nb", mode="overview")

Use with aai-coding

aai-coding owns the persistent Python session, hooks, ordinary Python files, configuration, and prose. nbskill.skill owns notebook source, generated modules, focused execution, and notebook review.

install_nbskill --target codex installs the local jupyter-notebooks skill. It never rewrites an aai-coding checkout or a README.

In an nbdev project, do not use exhash or a general editor to mutate a source notebook or its generated module. Import nbskill.skill, call generated_owner to route a Python file, read with context, query reference_query for nontrivial prior art, make one edit_notebook change, then prove it with exec_nb, diff_nb, and style_check.

References

References are a local implementation knowledgebase, not code to copy. Discover or register repositories with reference_discover or reference_add, ingest them with reference_ingest, then query them with reference_query before adding a nontrivial helper. Results combine structured filters, hybrid BM25/vector search, dependency status, and optional branch context.

reference_query(
    query="AST definition helper",
    top_k=5,
    include_branch=True,
)

Graph structure

The local graph records notebooks, cells, headings, symbols, and imports. Its edges record containment, definitions, calls, imports, documentation order, and related symbols. Call and import edges carry evidence separately from heuristic similarity edges.

This gives nbskill a fast, project-wide map of the notebook source. Agents can assess the impact of a change, follow a symbol to its callers, and spot cross-notebook private-helper use without reading every notebook. It is a navigation and maintenance aid, not a runtime dependency analyzer.

Ask for project context with the graph attached when you need to trace a symbol through the notebook structure:

context(target="project", scope="nbs", include_graph=True)

Problem-solution memory

Before choosing an approach, query problem_memory for similar verified lessons from this and other projects. After verification, add a reusable problem-solution pair with short evidence and at least four normalized tags, such as topic, sub-topic, library, problem category, and solution category.

Tags make lessons precise enough to reuse: a query with tags applies an exact all-of filter, while the natural-language problem ranks the remaining matches. This keeps a broadly similar memory from being mistaken for a directly applicable solution.

graph = notebook_knowledge_graph_data("nbs")
dict(nodes=len(graph["nodes"]), edges=len(graph["edges"]), node_types=sorted({node["type"] for node in graph["nodes"]}), edge_types=sorted({edge["type"] for edge in graph["edges"]}))
{'nodes': 3588,
 'edges': 13426,
 'node_types': ['cell', 'heading', 'import', 'notebook', 'symbol'],
 'edge_types': ['calls',
  'contains',
  'defines',
  'documents',
  'imports',
  'precedes',
  'similar_to']}