# Native nbskill Pyskill


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

``` python
from pathlib import Path
from nbskill import skill
from tempfile import gettempdir
from fastcore.nbio import mk_cell, read_nb
from nbskill.foundation import write_demo_notebook
from nbskill.skill import context
```

`nbskill.skill` is the direct API for notebook-owned code. Install
`nbskill` in the same Python environment that runs the agent, then
import it with `from nbskill.skill import *`. The `pyskills` entry point
in `pyproject.toml` makes the module discoverable after installation. An
entry point does not install the package. Use an editable install while
developing this checkout.

Reference queries use the machine-level `~/.nbskill/reference_knowledge`
directory across workspaces. Set `NBSKILL_REFERENCE_HOME` when a
separate reference index is required.

## Notebook contract

Treat one notebook as one part of a larger problem. Split that part into
H2 chapters. Each chapter tells one domain story from the motivating
problem through the implementation and its evidence. Put each chapter
heading in its own markdown cell so Jupyter can collapse the chapter as
a unit.

Give every public function its own `#| export` definition cell. Keep its
function bundle together in this order: a markdown description, the
definition, one executable example, and one focused test. The
description states the domain contract and the reason the function
belongs in the chapter. The example ends with the value a reader should
inspect. The test asserts one promised behavior. An example may contain
the assertion when that keeps the story clearer, but the definition
never shares a cell with imports, setup, examples, or tests.

Keep imports in import-only cells. Use `#| export` on the short markdown
summaries that must appear in the Pyskill module docstring. Use
`#| exportd` on compact runnable examples that must appear there as
fenced Python. `#| exportd` never adds the example to the generated
module.

``` python
function_bundle = [
    dict(cell_type="markdown", source=r'''### `parse_order`

Convert one order row into domain values.'''),
    dict(cell_type="code", source=r'''#| export
def parse_order(row):
    return dict(row)'''),
    dict(cell_type="code", source=r'''parsed = parse_order({"total": 12})
parsed'''),
    dict(cell_type="code", source=r'''assert parsed["total"] == 12'''),]
assert [cell["cell_type"] for cell in function_bundle] == ["markdown", "code", "code", "code"]
function_bundle
```

## Read notebooks

Start with notebook structure, then narrow to the chapter or symbol that
owns the work. Call
[`generated_owner`](https://MorsCerta-crypto.github.io/nbskill/foundation.html#generated_owner)
before touching a Python module. Call
[`context`](https://MorsCerta-crypto.github.io/nbskill/read.html#context)
to read the relevant notebook cells. Use `chapter=` for one domain story
and a qualified `#symbol` target for one definition. Call
[`reference_query`](https://MorsCerta-crypto.github.io/nbskill/knowledge.html#reference_query)
before adding a nontrivial helper.

### [`generated_owner`](https://MorsCerta-crypto.github.io/nbskill/foundation.html#generated_owner)

[`generated_owner`](https://MorsCerta-crypto.github.io/nbskill/foundation.html#generated_owner)
routes a generated Python module back to its source notebook. A `None`
result means the Python file is hand-written and stays on the ordinary
code path:

Use the imported module’s path to locate its notebook. nbdev runs these
examples from the notebook folder, so paths relative to the project root
would fail in CI.

``` python
owner = generated_owner(skill.__file__)
assert owner.name == "14_pyskill.ipynb"
owner
```

### [`context`](https://MorsCerta-crypto.github.io/nbskill/read.html#context)

[`context`](https://MorsCerta-crypto.github.io/nbskill/read.html#context)
reads notebooks as cells rather than raw JSON. Start with
`view="summary"`. Then request `view="full"` only for the chapter or
symbol that matters:

``` python
summary = context(owner, view="summary", verbose=False)
chapter = context(owner, chapter="Read notebooks", view="full", verbose=False)
assert summary["kind"] == "context"
assert chapter["selection"]["matches"]
chapter["selection"]["matches"]
```

### [`reference_query`](https://MorsCerta-crypto.github.io/nbskill/knowledge.html#reference_query)

[`reference_query`](https://MorsCerta-crypto.github.io/nbskill/knowledge.html#reference_query)
searches the shared reference index and direct dependencies. It uses the
optional `sentence-transformers` dependency. Install it with
`pip install sentence-transformers`, or use `uv sync --dev` in a source
checkout. Basic notebook operations do not import it. The following
example needs a populated reference index and a locally cached embedding
model.

Use
[`reference_query`](https://MorsCerta-crypto.github.io/nbskill/knowledge.html#reference_query)
before choosing a nontrivial parsing, notebook, abstract syntax tree,
filesystem, or formatting implementation:

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/knowledge.py#L1576"
target="_blank" style="float:right; font-size:smaller">source</a>

### reference_query

``` python
def reference_query(
    *args, **kwargs
):
```

*Search optional local references without loading that stack for basic
workflow use.*

``` python
matches = reference_query("find an nbdev notebook export helper", top_k=3)
matches["hits"]
```

## Write notebooks

Use one
[`edit_notebook`](https://MorsCerta-crypto.github.io/nbskill/edit.html#edit_notebook)
call for one coherent change. Read stable cell IDs and exact source text
first. Submit every related operation together so validation happens
before the notebook is written. Never splice notebook JSON or edit a
generated Python module.

### [`edit_notebook`](https://MorsCerta-crypto.github.io/nbskill/edit.html#edit_notebook)

[`edit_notebook`](https://MorsCerta-crypto.github.io/nbskill/edit.html#edit_notebook)
applies text and structural cell edits atomically.
[`replace_text`](https://MorsCerta-crypto.github.io/nbskill/edit.html#replace_text)
is the smallest edit when the old text is known. Structural operations
add, move, replace, or remove whole cells:

``` python
edits = [dict(op="replace_text", cell_id="6df2357f", old="direct API", new="direct notebook API")]
change = edit_notebook(owner, edits, dry_run=True)
assert change["changed"]
change["diffs"]
```

## Run notebooks

Run the smallest scope that proves the changed story.
[`exec_nb`](https://MorsCerta-crypto.github.io/nbskill/execute.html#exec_nb)
accepts a chapter name or an ending cell ID. Use `check_only=True` while
iterating so execution does not write outputs or metadata. Use
`allow_new=True` only when the new cells are trusted and must execute.

### [`exec_nb`](https://MorsCerta-crypto.github.io/nbskill/execute.html#exec_nb)

[`exec_nb`](https://MorsCerta-crypto.github.io/nbskill/execute.html#exec_nb)
executes the affected chapter in the project environment. Safe execution
is the default. `check_only=True` keeps the run in memory:

``` python
with write_demo_notebook(
    "run_lesson.ipynb", base=gettempdir(),
    cells=[mk_cell("## Answer", cell_type="markdown"), mk_cell("answer = 42\nassert answer == 42")],
) as run_path:
    run = exec_nb(run_path, chapter="Answer", check_only=True, allow_new=True, show_output=False)
    assert run["ok"], run["errors"]
    run_summary = dict(ok=run["ok"], written=run["dest"], executed=len(run["executed_cell_ids"]))
run_summary
```

### [`notebook_state`](https://MorsCerta-crypto.github.io/nbskill/state.html#notebook_state)

[`notebook_state`](https://MorsCerta-crypto.github.io/nbskill/state.html#notebook_state)
compares code cells with their last recorded execution and reports
likely stale downstream cells. Dynamic effects remain explicit under
`uncertainty`:

``` python
state = notebook_state(owner.with_name("15_state.ipynb"))
state["counts"]
```

## Validate notebooks

Validation combines execution evidence, a cell-level diff, and style
diagnostics. Run
[`exec_nb`](https://MorsCerta-crypto.github.io/nbskill/execute.html#exec_nb)
first. Read
[`diff_nb`](https://MorsCerta-crypto.github.io/nbskill/review.html#diff_nb)
as the source review and fix every relevant
[`style_check`](https://MorsCerta-crypto.github.io/nbskill/review.html#style_check)
diagnostic. Then run `nbdev-export` and verify the generated module from
a clean interpreter. The notebook remains the source of truth.

### [`diff_nb`](https://MorsCerta-crypto.github.io/nbskill/review.html#diff_nb)

[`diff_nb`](https://MorsCerta-crypto.github.io/nbskill/review.html#diff_nb)
shows changed cell sources without raw notebook JSON, output noise, or
unrelated metadata:

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/review.py#L1204"
target="_blank" style="float:right; font-size:smaller">source</a>

### diff_nb

``` python
def diff_nb(
    *args, **kwargs
):
```

*Show source changes without loading review dependencies until needed.*

``` python
review = diff_nb(owner, ref_a="HEAD")
review
```

### [`style_check`](https://MorsCerta-crypto.github.io/nbskill/review.html#style_check)

[`style_check`](https://MorsCerta-crypto.github.io/nbskill/review.html#style_check)
reports notebook hygiene and source diagnostics. Restrict it to changed
cells while iterating, then check the whole notebook before export:

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/review.py#L904"
target="_blank" style="float:right; font-size:smaller">source</a>

### style_check

``` python
def style_check(
    *args, **kwargs
):
```

*Run style diagnostics without loading review dependencies until
needed.*

``` python
diagnostics = style_check(owner, changed_only=True)
diagnostics
```

### Direct workflow check

This test uses the public Pyskill to make a structured change to a fresh
notebook. It protects the direct workflow from accidental undeclared
dependencies.
