A persistent Python REPL for AI agents, driven from the CLI
Project description
agentnb
A persistent project-scoped Python REPL for coding agents, exposed through a simple CLI.
Status: alpha. Expect rough edges and breaking changes. Use in local development workflows at your own risk.
Why
Agents can run shell commands, but they lose state when using one-off python -c and script invocations. agentnb gives agents a long-running IPython kernel they can drive with CLI commands, so they can explore incrementally, keep expensive setup in memory, inspect live variables, and recover without restarting from scratch on every step.
The right mental model is a persistent REPL for agents, or an append-only notebook without a notebook UI. agentnb keeps execution state and history, but it does not edit notebook cells or manage .ipynb files.
Module reloading is explicit. agentnb does not auto-reload edited modules on
every execution; use agentnb reload after changing project-local code.
exec follows normal IPython/Notebook semantics for output: if the final line
of a snippet is an expression, its value is returned as the execution result.
print(...) goes to stdout; a bare final expression goes to result.
One session should be driven serially. Do not send multiple commands to the same project kernel at once; wait for one command to finish before sending the next.
Install
uv add agentnb
# or
pip install agentnb
Running Code
Run from the target project root when possible. The cheapest path is the implicit top-level execution form:
agentnb "from myapp.models import User"
agentnb "User.query.limit(5)"
For multiline code or code containing braces, quotes, or special shell characters, prefer stdin/heredoc over inline strings:
agentnb <<'PY'
import pandas as pd
df = pd.read_csv("tips.csv")
df.describe()
PY
You can also run a script directly, and then keep the state in session so you can poke around:
agentnb analysis.py
agentnb "print(final_result)"
--session and --background work in prefix position for inline code, file
execution, and most subcommands. Putting them after the subcommand name always
works:
agentnb --session myenv "df.head()" # prefix works for inline exec
agentnb --background "long_task()"
agentnb history --session myenv
agentnb runs list --session myenv
The default execution timeout is 30 seconds. Use --timeout for long-running
code:
agentnb --timeout 120 "train_model()"
Use --stream to see execution output in real time:
agentnb --stream "train_model(epochs=10)"
Use --fresh to stop and restart the session before executing, ensuring a
clean slate:
agentnb --fresh "from myapp import run; run()"
Use --no-truncate to get full stdout/stderr/result output in --agent mode
instead of the default compact truncation:
agentnb --agent --no-truncate "print(large_output)"
Reading Results And Inspecting State
agentnb follows normal IPython/Notebook behavior: a final expression becomes the
execution result, while print(...) goes to stdout.
Human-oriented output:
$ agentnb "x = 40 + 2; x"
42
$ agentnb "print('hello')"
hello
Use vars to see the current namespace and inspect to drill into one value:
agentnb vars
agentnb vars --recent 5
agentnb vars --match rows
agentnb inspect df
Reloading Local Imports
agentnb does not auto-reload edited modules by default. But after changing
project-local code on disk, you can reload explicitly:
agentnb reload
agentnb reload myapp.models
Bare reload reloads imported project-local modules and reports rebound names
and possible stale objects. reload MODULE targets one imported module. This allows you to
try out local functions as you improve them and reload them.
Background Runs And History
Use wait to just wait for the last command to finish:
agentnb wait
You can also check status with built-in wait modes:
agentnb status --wait # wait until the session is ready
agentnb status --wait-idle # wait until idle (not executing)
Use --background when you want to start work and come back to it later:
agentnb --background "long_task()"
That command returns quickly with an execution_id. agentnb also records a
durable run record for that execution, so you can look it up again even after
the original command has finished printing output.
Use runs when the question is about one specific execution:
- "What is the latest stored state of this run?"
- "Show me live progress from the active run."
- "Wait until that run finishes."
- "Cancel the active run."
The common runs commands are:
agentnb runs show
agentnb runs follow
agentnb runs wait
agentnb runs cancel @active
How to read them:
runs showreturns the latest persisted snapshot for a runruns followreplays all output so far, then streams new events until done; use--tailto skip history and only stream new eventsruns waitblocks until a run finishes and returns its final stateruns cancelrequests cancellation for an active run
Filter the runs list with --last N and --errors:
agentnb runs list --last 5
agentnb runs list --errors
By default, omitted run references mean "use the obvious next run":
runs showfalls back to the current session's latest run, then the project latestruns follow,runs wait, andruns cancelonly auto-target an active run
Use an explicit id or selector when you want an exact lookup instead of the cheap default:
agentnb runs show run_123
agentnb runs wait @latest
agentnb runs show @last-success
agentnb runs show @last-error
Selectors supported by runs are @latest, @active, @last-error, and
@last-success.
history is related, but it answers a different question.
Use history when you want the semantic transcript of what you asked agentnb
to do, not the low-level run record for one execution. It includes user-visible
steps such as exec, vars, inspect, reload, and reset:
agentnb history
agentnb history --last 5
agentnb history --errors
agentnb history --latest
agentnb history @last-error
agentnb history @last-success
agentnb history --all
agentnb history --full # full un-truncated code and output
The difference is:
runsis for exact execution records and background-run controlhistoryis for the notebook-like semantic transcript of your workflow
A simple rule:
- If you care about one
execution_id, useruns - If you care about the recent flow of work in the session, use
history
Output Modes
Default output is plain terminal text, not JSON.
What that means in practice:
- for
exec, you usually just see the produced output:stdout,stderr, and a finalresultif there is one - for status-style commands such as
start,status,wait,stop, andinterrupt, you get a short sentence - for commands such as
vars,history, andruns, you get a compact text listing
Examples:
$ agentnb "1 + 1"
2
$ agentnb "print('hello')"
hello
$ agentnb wait
Kernel is idle (session: default, pid 91098).
Use --json when you want the full stable payload for scripting:
agentnb --json "1 + 1"
agentnb runs show @latest --json
Example:
{
"schema_version": "1.0",
"status": "ok",
"command": "exec",
"project": "/path/to/project",
"session_id": "default",
"timestamp": "2026-03-16T20:26:15.207123+00:00",
"data": {
"duration_ms": 16,
"status": "ok",
"execution_id": "919910f9e8e1",
"execution_count": 12,
"result": "2",
"ensured_started": true,
"started_new_session": false
},
"suggestions": [],
"error": null
}
Use --agent when you still want JSON, but a smaller payload:
agentnb --agent "1 + 1"
agentnb runs follow --agent
Example:
{
"ok": true,
"command": "exec",
"session_id": "default",
"data": {
"status": "ok",
"execution_id": "31690003f3c0",
"duration_ms": 50,
"ensured_started": true,
"started_new_session": false,
"result": "2",
"result_json": 2
}
}
If you truly want only one stream from exec, use the output selectors
instead:
agentnb --result-only "1 + 1"
agentnb "print('hello')" --stdout-only
agentnb --stderr-only "raise RuntimeError('boom')"
Typical scripting patterns:
agentnb --agent "1 + 1" | jq .
agentnb --json "1 + 1" | python -c 'import json,sys; print(json.load(sys.stdin)["data"]["result"])'
If you want a default mode per shell:
export AGENTNB_FORMAT=agent
AGENTNB_FORMAT=agent enables compact JSON output and suppresses routine
suggestions. AGENTNB_FORMAT=json selects the full stable envelope.
Use --quiet to suppress non-essential human output, or --no-suggestions to
hide next-step suggestions:
agentnb --quiet "1 + 1"
agentnb --no-suggestions "1 + 1"
Top-level flags such as --agent, --json, --quiet, and --no-suggestions
can appear before or after the subcommand.
Use --agent or --json when consuming output programmatically to get a
stable, parseable single JSON object on stdout:
Recovery And Lifecycle
Use the lifecycle commands based on the failure mode:
agentnb start
agentnb status
agentnb wait
agentnb interrupt
agentnb reset
agentnb stop
agentnb doctor
agentnb doctor --fix
Use:
interruptwhen code hangs but the kernel should surviveresetwhen the namespace is polluted but the process is still healthystopand thenstartwhen the kernel is dead or badly wedgeddoctorwhen startup or environment detection fails
On agentnb start, the runtime selects an interpreter in this order:
--python<project>/.venv- active
VIRTUAL_ENV - current Python executable
If ipykernel is missing for the selected interpreter, agentnb start fails
with the exact install command. Pass --auto-install to let agentnb install
it, or use agentnb doctor --fix.
Projects And Sessions
If you are already in the target project root, you usually do not need
--project.
Use --project when you are driving another repo from this checkout or from
some other working directory:
uv run agentnb --project /path/to/project "from myapp.models import User"
uv run agentnb --project /path/to/project runs follow --agent
Use --session when you want more than one live kernel for the same project:
agentnb --session analysis "1 + 1"
agentnb start --session analysis
agentnb sessions list # bare `agentnb sessions` shows help, not the list
agentnb sessions delete analysis
agentnb sessions delete --stale # delete sessions with dead kernels
agentnb sessions delete --all # delete all sessions
When only one live session exists, kernel-bound commands can infer it. Once
multiple live sessions exist, pass --session NAME explicitly.
How It Works
agentnb starts an IPython kernel process and stores connection/session metadata under .agentnb/ in the target project. CLI commands connect via Jupyter messaging protocol.
Architecture
SessionStore: project/session metadata and stale cleanupExecutionStore: append-only JSONL run records keyed byexecution_idExecutionService: foreground/background execution lifecycle and run queriesCommandJournal: unified read path over semantic history and persisted execution recordsHistoryStore: typed JSONL history records for semantic and internal execution historyKernelRuntime: lifecycle + execution APIRuntimeBackend: backend interface, with local IPython backend for v0.1NotebookOps: vars/inspect/reload semantic operationsOutputContract: human + JSON output from one response envelopeHooks: no-op extension points for future policy/plugins/telemetry
Development
uv sync --extra dev
uv run ruff check src tests
uv run ruff format --check src tests
uv run ty check src tests
uv run pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentnb-0.3.4.tar.gz.
File metadata
- Download URL: agentnb-0.3.4.tar.gz
- Upload date:
- Size: 82.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0193fabe0f6b79da46291155945117ad7c074b879f6077b904bad5add178449c
|
|
| MD5 |
b0ed0ffea1be5e764c60e55798173513
|
|
| BLAKE2b-256 |
b0dd2a49a78e50c2383b72fae7268615a47e9f05085bd565c0d8572712ac3bb9
|
Provenance
The following attestation bundles were made for agentnb-0.3.4.tar.gz:
Publisher:
publish.yml on oegedijk/agentnb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentnb-0.3.4.tar.gz -
Subject digest:
0193fabe0f6b79da46291155945117ad7c074b879f6077b904bad5add178449c - Sigstore transparency entry: 1138527607
- Sigstore integration time:
-
Permalink:
oegedijk/agentnb@99319a2e22ca9859064e4b381c70d41a4097ec35 -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/oegedijk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@99319a2e22ca9859064e4b381c70d41a4097ec35 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agentnb-0.3.4-py3-none-any.whl.
File metadata
- Download URL: agentnb-0.3.4-py3-none-any.whl
- Upload date:
- Size: 99.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a74f0518868cedb5e6147435b4ec16961a1bb7023c65fa382593be56b174352e
|
|
| MD5 |
e713a05a0ea509700bedcda63a4d9d75
|
|
| BLAKE2b-256 |
278651895c0d5fc02ee8d3f5443d33268a7f2119469e4f540b7c7087f4bdf2cf
|
Provenance
The following attestation bundles were made for agentnb-0.3.4-py3-none-any.whl:
Publisher:
publish.yml on oegedijk/agentnb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentnb-0.3.4-py3-none-any.whl -
Subject digest:
a74f0518868cedb5e6147435b4ec16961a1bb7023c65fa382593be56b174352e - Sigstore transparency entry: 1138527675
- Sigstore integration time:
-
Permalink:
oegedijk/agentnb@99319a2e22ca9859064e4b381c70d41a4097ec35 -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/oegedijk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@99319a2e22ca9859064e4b381c70d41a4097ec35 -
Trigger Event:
push
-
Statement type: