<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Piyush Jain</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/author-piyush-jain.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2026-05-11T19:04:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>nb-cli: A Command-Line Interface for AI Agents and Notebook Automation</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/" rel="alternate"/><published>2026-05-11T19:04:00+00:00</published><updated>2026-05-11T19:04:00+00:00</updated><author><name>Piyush Jain</name></author><id>tag:jasongrout.github.io,2026-05-11:/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/</id><summary type="html">&lt;p&gt;The rise of AI coding agents has transformed how we think about developer tools. Large language models like Claude, GPT, and others are…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The rise of AI coding agents has transformed how we think about developer tools. Large language models like Claude, GPT, and others are remarkably effective at using command-line interfaces — they’ve been trained on billions of lines of CLI usage from documentation, Stack Overflow, and GitHub. But when it comes to working with Jupyter notebooks programmatically, there’s been a gap: existing tools focus on running agents within notebooks, but what about agents that need to work with notebooks as artifacts?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyter-ai-contrib/nb-cli"&gt;&lt;strong&gt;nb-cli&lt;/strong&gt;&lt;/a&gt;, an experimental open-source command-line interface designed specifically for AI agents, automation scripts, and developers who need programmatic access to Jupyter notebooks. Built with Rust for performance and reliability, nb-cli provides a fast, composable way to read, write, execute, and manipulate notebooks through a clean command line interface that follows the &lt;a href="https://nbformat.readthedocs.io/en/latest/"&gt;nbformat&lt;/a&gt; specification.&lt;/p&gt;
&lt;h2 id="the-problem-notebooks-as-black-boxes"&gt;&lt;strong&gt;The Problem: Notebooks as Black Boxes&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;While Jupyter notebooks are indispensable for interactive exploration, their underlying &lt;code&gt;.ipynb&lt;/code&gt; JSON structure has long been a friction point for programmatic interaction, especially for shell scripts and Large Language Models (LLMs).&lt;/p&gt;
&lt;p&gt;Traditional workflows often break down when automation or AI-driven analysis is required. Consider the following scenarios where standard notebook interfaces prove insufficient:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Autonomous Analysis&lt;/strong&gt;: An AI agent tasked with auditing a data science workflow must programmatically inspect individual cells to map out the analysis pipeline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated Validation&lt;/strong&gt;: CI/CD systems require a reliable method to execute notebooks, validate outputs, and catch errors before deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation at Scale&lt;/strong&gt;: Developers need tools to automatically transform notebook content into clean, accessible documentation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production Debugging&lt;/strong&gt;: Teams need a way to troubleshoot notebook execution failures in headless production environments without manual intervention.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notebooks as Data&lt;/strong&gt;: Analysts may want to treat a notebook as a structured database to programmatically generate business reports, research summaries, or custom visualizations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Historically, solving these problems required labor-intensive workarounds like manually navigating the JupyterLab UI, writing brittle Python scripts to parse complex JSON files, or using execution tools that lack real-time integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;nb-cli&lt;/strong&gt; bridges this gap by offering a CLI-first interface designed for the modern era of automation. By leveraging command-line patterns and Unix composability, it provides the structured output and predictable interface that AI agents and developers need to treat notebooks as first-class citizens in any software stack.&lt;/p&gt;
&lt;h3 id="key-features"&gt;Key Features&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Works With or Without a Jupyter Server&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;nb-cli doesn’t require a running Jupyter server. By default, it reads and writes &lt;code&gt;.ipynb&lt;/code&gt; files directly and communicates with kernels over ZeroMQ for execution. This makes it well-suited for scripting, CI pipelines, and any workflow where launching a server is unnecessary overhead.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a notebook — no server needed&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb

&lt;span class="c1"&gt;# Add cells&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import pandas as pd&amp;quot;&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;# Data Analysis&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--type&lt;span class="w"&gt; &lt;/span&gt;markdown

&lt;span class="c1"&gt;# Execute&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb

&lt;span class="c1"&gt;# Read back with outputs&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Connecting to a server becomes valuable if multiple users and/or agents are editing the same notebook simultaneously within a JupyterLab session. Once connected, nb-cli uses Y.js, the same CRDT protocol JupyterLab uses internally for conflict-free real-time synchronization.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Auto-detect and connect to local Jupyter server&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;connect

&lt;span class="c1"&gt;# Or, connect to a specific server&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;connect&lt;span class="w"&gt; &lt;/span&gt;--server&lt;span class="w"&gt; &lt;/span&gt;http://localhost:9999&lt;span class="w"&gt; &lt;/span&gt;--token&lt;span class="w"&gt; &lt;/span&gt;abc

&lt;span class="c1"&gt;# Add a cell - it appears instantly in JupyterLab&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;experiment.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;df.head()&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Execute via the remote kernel&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;experiment.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell&lt;span class="w"&gt; &lt;/span&gt;fe456

&lt;span class="c1"&gt;# Restart the kernel before execution for reproducibility checks&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;experiment.ipynb&lt;span class="w"&gt; &lt;/span&gt;--restart-kernel

&lt;span class="c1"&gt;# Disconnect&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;disconnect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When connected to a Jupyter server, &lt;strong&gt;nb-cli&lt;/strong&gt; detects whether a notebook is open in JupyterLab and uses server APIs for conflict-free collaborative editing. If the notebook isn’t open, it seamlessly falls back to file-based operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-Optimized Markdown Format&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Language models don’t parse JSON, they predict tokens. This distinction matters more than you’d think when you’re building a tool that feeds notebook content into an LLM’s context window. Jupyter’s native notebook format is deeply nested JSON. Source code is stored as arrays of strings. Outputs carry base64-encoded blobs. Metadata nests several levels deep. This is fine for a JSON parser, but for a language model working within a fixed context window, 30–40% of those tokens are structural characters — braces, brackets, escaped newlines that carry no semantic value. Another common option is plain Markdown which is token-efficient and human-readable, but it’s ambiguous. A # could be a markdown heading or a Python comment. A fenced code block could be a notebook cell or an example inside a markdown cell’s documentation. When an LLM is asked to “fix the error in cell 7,” it needs to reliably locate that cell in the text and plain markdown gives it no structural markers to count on, just a sequence of code fences that all look the same.&lt;/p&gt;
&lt;p&gt;So we designed a line-oriented sentinel format:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="kc"&gt;n&lt;/span&gt;&lt;span class="err"&gt;o&lt;/span&gt;&lt;span class="kc"&gt;te&lt;/span&gt;&lt;span class="err"&gt;book&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ai-notebook&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;metadata&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;kernelspec&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;python3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;

&lt;span class="err"&gt;@@cell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;f68t57&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;cell_type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;execution_count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;```py&lt;/span&gt;&lt;span class="kc"&gt;t&lt;/span&gt;&lt;span class="err"&gt;ho&lt;/span&gt;&lt;span class="kc"&gt;n&lt;/span&gt;
&lt;span class="err"&gt;d&lt;/span&gt;&lt;span class="kc"&gt;f&lt;/span&gt;&lt;span class="err"&gt;.head()&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;
&lt;span class="err"&gt;@@ou&lt;/span&gt;&lt;span class="kc"&gt;t&lt;/span&gt;&lt;span class="err"&gt;pu&lt;/span&gt;&lt;span class="kc"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;output_type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;execute_result&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="kc"&gt;te&lt;/span&gt;&lt;span class="err"&gt;x&lt;/span&gt;&lt;span class="kc"&gt;t&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;col_a&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;col_b&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;
&lt;span class="err"&gt;```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The format makes a few deliberate tradeoffs. &lt;strong&gt;@@cell&lt;/strong&gt; and &lt;strong&gt;@@output&lt;/strong&gt; sentinels give the model unambiguous structural boundaries without counting braces or tracking nesting. Inline JSON metadata on each sentinel line places cell type, index, and execution count in the tokens immediately before the content — matching how attention mechanisms locate information. Code in fenced blocks with language hints activates the model’s syntax-level training. And because each cell block is self-contained, truncation degrades gracefully — unlike JSON, where a cut anywhere breaks the entire structure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Designed for Composability&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;nb-cli follows Unix conventions — plain text output, stdin support, predictable exit codes — so it composes naturally with other CLI tools. For AI agents, this matters because a single shell command can replace what would otherwise be multiple tool calls with intermediate parsing.&lt;/p&gt;
&lt;p&gt;Consider an agent asked to “add a summary section to the notebook and run it.” Without nb-cli, this requires separate API calls to read the notebook, parse the structure, insert a cell, write the file, find a kernel, execute, read back the output — each consuming tokens for the request and response. With nb-cli:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;                                                                         &lt;/span&gt;
&lt;span class="s"&gt;@@markdown                                                                                                                 &lt;/span&gt;
&lt;span class="s"&gt;# Summary                                                                                                                  &lt;/span&gt;
&lt;span class="s"&gt;                                                                                                                            &lt;/span&gt;
&lt;span class="s"&gt;@@code                                                                                                                     &lt;/span&gt;
&lt;span class="s"&gt;print(f&amp;quot;Rows: {len(df)}, Columns: {len(df.columns)}&amp;quot;)                                                                      &lt;/span&gt;
&lt;span class="s"&gt;df.describe()                                                                                                              &lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                        &lt;/span&gt;
&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-2&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Three operations — add cells, execute them, read the result — in a single shell invocation. The agent gets back only the output it needs without re-reading the entire notebook.&lt;/p&gt;
&lt;p&gt;The same principle applies to debugging. An agent investigating a failed notebook doesn’t need to read every cell to find the problem.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Find cells with errors — returns only the relevant cells                                                                 &lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--with-errors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One call, targeted output, no wasted tokens on cells that ran successfully.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stable Cell Referencing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;nb-cli supports two ways to reference cells.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Index-based&lt;/strong&gt;:&lt;code&gt;--cell-index 0&lt;/code&gt; (supports negative indexing: -1 = last cell)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ID-based&lt;/strong&gt;: &lt;code&gt;--cell f68t57&lt;/code&gt; (doesn’t change when cells move)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Reference by position&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell-index&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x = 42&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Reference by stable ID — safe even after cells are reordered&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell&lt;span class="w"&gt; &lt;/span&gt;ce456&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;print(&amp;#39;Done&amp;#39;)&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Execute the last cell&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell-index&lt;span class="w"&gt; &lt;/span&gt;-1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Powerful Search Capabilities&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;nb-cli includes built-in search to quickly locate cells by content, type, or execution errors. By default, search matches against cell source code, but a scope filter extends it to execution outputs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Search for cells containing a pattern  &lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import pandas&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;
&lt;span class="c1"&gt;# Find all cells with execution errors&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--with-errors&lt;span class="w"&gt;                                                                                                     &lt;/span&gt;

&lt;span class="c1"&gt;# Search within outputs instead of source                                                                                       &lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;KeyError&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--scope&lt;span class="w"&gt; &lt;/span&gt;output

&lt;span class="c1"&gt;# Filter by cell type&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;TODO&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--cell-type&lt;span class="w"&gt; &lt;/span&gt;mardown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For AI agents, — with-errors is particularly useful — instead of reading an entire notebook to find what failed, the agent gets back only the cells that need attention. Combined with — scope output, it can search error tracebacks directly without parsing every cell’s results. The same capabilities are useful for humans auditing notebooks for deprecated APIs, locating specific functions across large notebooks, or extracting patterns before a refactor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-Cell Operations&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One of the most common patterns when working with notebooks programmatically is adding a sequence of cells — a markdown header, then setup code, then analysis. Doing this one cell at a time means multiple round-trips and index bookkeeping. Instead, nb-cli accepts multiple cells in a single call using the sentinels format we saw earlier.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add a markdown header followed by a code cell in one command&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;report.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;@@markdown&lt;/span&gt;
&lt;span class="s"&gt;# Results&lt;/span&gt;

&lt;span class="s"&gt;@@code&lt;/span&gt;
&lt;span class="s"&gt;import pandas as pd&lt;/span&gt;
&lt;span class="s"&gt;df = pd.read_csv(&amp;#39;results.csv&amp;#39;)&lt;/span&gt;
&lt;span class="s"&gt;df.head()&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more control, sentinels also accept the full @@cell {“cell_type”: “…”} JSON format.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;report.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;                                     &lt;/span&gt;
&lt;span class="s"&gt;@@cell {&amp;quot;cell_type&amp;quot;: &amp;quot;markdown&amp;quot;}                                                     &lt;/span&gt;
&lt;span class="s"&gt;# Analysis Header                                                                     &lt;/span&gt;

&lt;span class="s"&gt;@@cell {&amp;quot;cell_type&amp;quot;: &amp;quot;code&amp;quot;}                                                         &lt;/span&gt;
&lt;span class="s"&gt;print(&amp;quot;hello&amp;quot;)&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;&lt;span class="w"&gt;                                                                &lt;/span&gt;
&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Both formats work with stdin, making it easy to compose cells from scripts or pipelines.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@@markdown\n## Summary\n\n@@code\ndf.describe()\n&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;report.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The same batching philosophy extends to execution and deletion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Execute cells 2 through 5&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--start&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--end&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;

&lt;span class="c1"&gt;# Delete specific cells&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;delete&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="c1"&gt;# Delete a range of cells&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;delete&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--range&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;:3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Environment-Aware Execution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;— uv&lt;/strong&gt; and &lt;strong&gt;— pixi&lt;/strong&gt; flags are supported on &lt;strong&gt;nb connect&lt;/strong&gt;, &lt;strong&gt;nb execute&lt;/strong&gt;, and &lt;strong&gt;nb create&lt;/strong&gt;, telling nb to discover Jupyter servers and kernels through the appropriate environment manager. &lt;strong&gt;nb status — python&lt;/strong&gt; returns the command prefix needed to run Python in the same environment as the connected kernel — useful when agent-generated shell commands need to match the active notebook environment.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Connect using a uv-managed environment&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;connect&lt;span class="w"&gt; &lt;/span&gt;--uv

&lt;span class="c1"&gt;# Execute in a pixi-managed environment&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--pixi

&lt;span class="c1"&gt;# Get the Python prefix for agent-generated shell commands&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;--python
&lt;span class="c1"&gt;# Returns: &amp;quot;uv run&amp;quot;, &amp;quot;pixi run&amp;quot;, or empty for system Python&lt;/span&gt;

&lt;span class="c1"&gt;# Use in a pipeline&lt;/span&gt;
&lt;span class="k"&gt;$(&lt;/span&gt;nb&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;--python&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import pandas; print(pandas.__version__)&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="real-world-use-cases"&gt;Real World Use Cases&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;AI Agent Workflows&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AI coding agents can now manipulate notebooks as a part of their analysis workflow.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Surface all failing cells&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;data_analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--with-errors

&lt;span class="c1"&gt;# Apply the fix&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;data_analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell-index&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;df = pd.read_csv(&amp;#39;data.csv&amp;#39;, encoding=&amp;#39;utf-8&amp;#39;)&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Re-execute to verify&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;data_analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell-index&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;CI/CD Integration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Automated testing and validation of notebooks in continuous integration pipelines.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Executing notebook...&amp;quot;&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;pipeline.ipynb&lt;span class="w"&gt; &lt;/span&gt;--allow-errors

&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Checking for errors...&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;pipeline.ipynb&lt;span class="w"&gt; &lt;/span&gt;--with-errors&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Notebook execution failed&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Clearing outputs before commit...&amp;quot;&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;output&lt;span class="w"&gt; &lt;/span&gt;clear&lt;span class="w"&gt; &lt;/span&gt;pipeline.ipynb

&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;✓ All cells executed successfully&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Programmatic Notebook Generation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Generate documentation, reports, and analysis automatically.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a report notebook&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;report.ipynb

&lt;span class="c1"&gt;# Add title, introduction, and analysis in one multi-cell command&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;cell&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;report.ipynb&lt;span class="w"&gt; &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;@@markdown&lt;/span&gt;
&lt;span class="s"&gt;# Monthly Sales Report&lt;/span&gt;

&lt;span class="s"&gt;@@markdown&lt;/span&gt;
&lt;span class="s"&gt;Generated on $(date)&lt;/span&gt;

&lt;span class="s"&gt;@@code&lt;/span&gt;
&lt;span class="s"&gt;import pandas as pd&lt;/span&gt;
&lt;span class="s"&gt;df = pd.read_csv(&amp;#39;sales_data.csv&amp;#39;)&lt;/span&gt;
&lt;span class="s"&gt;df.describe()&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Execute to populate outputs&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;report.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Debugging Production Notebooks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Quickly inspect and diagnose issues in deployed notebooks.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Find all cells with errors&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;failing_notebook.ipynb&lt;span class="w"&gt; &lt;/span&gt;--with-errors

&lt;span class="c1"&gt;# Search for cells with deprecated API usage&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;analysis.ipynb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pandas.np&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Find cells with potential security issues&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;notebook.ipynb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;eval(&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Examine specific failing cell with full output&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;failing_notebook.ipynb&lt;span class="w"&gt; &lt;/span&gt;--cell-index&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;

&lt;span class="c1"&gt;# Restart kernel and re-run for clean reproducibility check&lt;/span&gt;
nb&lt;span class="w"&gt; &lt;/span&gt;execute&lt;span class="w"&gt; &lt;/span&gt;failing_notebook.ipynb&lt;span class="w"&gt; &lt;/span&gt;--restart-kernel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="nb-cli-in-action"&gt;nb-cli in Action&lt;/h3&gt;
&lt;p&gt;To illustrate how AI agents use nb-cli naturally, here are some examples of agent interactions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1&lt;/strong&gt;: Claude creating a RL for LLMs notebook&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;User Prompt&lt;/strong&gt;&lt;/em&gt;: Help me learn about reinforcement learning for LLMs by creating a notebook and explaining at each cell how it all works. Cover the key concepts: policy model, reward model, KL divergence penalty, PPO, and GRPO. Use a tiny toy model (small vocab, GRU-based) so everything runs on CPU without any API keys.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/images/001-1_TGHiiVA5yiE4HBdvSAZ6vg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 2&lt;/strong&gt;: Codex fixing multiple bugs in a notebook&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;User Prompt&lt;/strong&gt;&lt;/em&gt;: The file churn_analysis.ipynb is a broken research notebook that was last updated in 2023. Fix it so it runs cleanly end-to-end. Identify every cell that fails, fix each issue and verify the notebook executes successfully. After fixing, add a brief markdown note above each cell you changed explaining what was broken and why.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/images/002-1_VrpFHv9vbJCo-SByBhAwmQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Codex fixed four bugs in churn_analysis.ipynb: a hardcoded file path, DataFrame.append() (removed in pandas 2.0), sklearn.cross_validation (removed in sklearn 0.20), and plot_confusion_matrix (removed in sklearn 1.2) and verified the notebook runs end-to-end after these chages.&lt;/p&gt;
&lt;h3 id="getting-started-with-nb-cli"&gt;Getting Started with nb-cli&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Use the install script.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-fsSL&lt;span class="w"&gt; &lt;/span&gt;https://raw.githubusercontent.com/jupyter-ai-contrib/nb-cli/main/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If your platform is not supported, and you get an error during install, use cargo to install.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;cargo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nb-cli&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or build from source.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/jupyter-ai-contrib/nb-cli.git
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nb-cli
cargo&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The binary will be available at &lt;code&gt;target/release/nb&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;To enable your AI agents to use nb for all notebook operations, install the skill.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npx&lt;span class="w"&gt; &lt;/span&gt;skills&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;jupyter-ai-contrib/nb-cli
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="about-the-developers"&gt;About the developers&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt="Andrii Ieroshenko is a Software Development Engineer at AWS. He is a long term contributor to project Jupyter working on JupyterLab, Jupyter AI and several other projects. He is also a member of the Jupyter Media Strategy Working Group." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/images/003-0_i4frZE6ZuXt6owQy.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;a href="https://github.com/andrii-i/"&gt;Andrii Ieroshenko&lt;/a&gt; is a Software Development Engineer at AWS. He is a long term contributor to project Jupyter working on JupyterLab, Jupyter AI and several other projects. He is also a member of the Jupyter Media Strategy Working Group.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Brian Granger is a Senior Principal Technologist at AWS. Brian is a cofounder of Project Jupyter, a board member of the Jupyter and PyTorch Foundations." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/images/004-0_OkFJLojf_qblg8Uv.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;a href="https://github.com/ellisonbg"&gt;Brian Granger&lt;/a&gt; is a Senior Principal Technologist at AWS. Brian is a cofounder of Project Jupyter, a board member of the Jupyter and PyTorch Foundations.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Piyush Jain is a Principal Engineer at AWS working on Jupyter and Agentic AI. He is a distinguished Jupyter contributor and a member of the Jupyter Server Council." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/nb-cli-a-command-line-interface-for-ai-agents-and/images/005-0__uwMJpQQ-GZTs1Fh.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;a href="https://github.com/3coins"&gt;Piyush Jain&lt;/a&gt; is a Principal Engineer at AWS working on Jupyter and Agentic AI. He is a distinguished Jupyter contributor and a member of the Jupyter Server Council.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="how-can-you-help"&gt;How can you help?&lt;/h3&gt;
&lt;p&gt;We’re just getting started with &lt;strong&gt;nb-cli&lt;/strong&gt;. Please join us!.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Install and use the nb-cli.&lt;/strong&gt; If you find any bugs or have suggestions, please create issues on GitHub.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Join the discussion&lt;/strong&gt; about nb-cli, open issues or add to discussion in &lt;a href="https://github.com/orgs/jupyter-ai-contrib/discussions"&gt;jupyter-ai-contrib&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribute:&lt;/strong&gt; Your bug reports, feature requests, and pull requests will help improve this project for everyone.&lt;/li&gt;
&lt;/ul&gt;
</content><category term="AI"/></entry></feed>