Repository navigation
Releases
v0.2.3 - 技能保真度:点明被编辑的 code 格会补上 outputs/execution_count
[0.2.3] - 2026-10-15
Fixed
- The skill's fidelity section claimed that nothing is ever invented, full stop.
The cell you edit is the exception: editing a code cell writesoutputsand
execution_count— that is what "clear the outputs" means — so a cell that
carried neither key gains both, appended at the end of its key order. Only
untouched cells keep their exact shape. The wording now says so, bullet by
bullet, instead of leaving the reader to discover it from a byte diff.
Verified end to end against a hand-written nbformat 4.4 file (2-space indent,
CRLF, no trailing newline, no cell ids): the untouched cells kept their array
sourceand theiroutputs/execution_countand were not given anid; the
file kept its indent, its CRLF and its missing trailing newline; the edited
cell alone came back with the two keys appended.
v0.2.2 - compare the peeled tag commit so re-runs are a no-op
[0.2.2] - 2026-10-15
Fixed
npm run releasecompared the tag with HEAD through the bare ref, which for
an annotated tag yields the tag object rather than the commit it marks — so
a tag that did point at HEAD was reported as pointing somewhere else, and
re-running a finished release failed. It now compares the peeled commit
(refs/tags/v<x.y.z>^{commit}). As a result a completed release is a clean
no-op: the push and the tag are skipped and the existing GitHub Release is
reported instead of being posted a second time.
Added
test/release.test.jspins the peeling down against a throwaway repository.
v0.2.1 - npm run release: tag, push and GitHub Release in one command
[0.2.1] - 2026-10-15
Releases are a script now, not a habit: npm run release verifies the version
against the CHANGELOG, runs the tests, tags, pushes, and publishes the GitHub
Release.
Added
npm run release(scripts/release.mjs): reads the version from
package.jsonand refuses to run unlessCHANGELOG.mdcarries a matching,
non-empty## [x.y.z]section and a[x.y.z]:link reference; requires a
clean working tree on the release branch with the version bump already
committed; runsnode --test; pushes the release branch; creates the
annotated tagv<x.y.z>; pushes it; and creates the GitHub Release through
the REST API, using the tag annotation as the release title and the CHANGELOG
section as its body.
Credentials come fromGITHUB_TOKEN/GH_TOKENorgit credential fill
and are never printed. Flags:--title,--dry-run,--skip-tests,
--branch,--remote.test/release.test.js: covers the argument parser, remote URL parsing,
CHANGELOG section and link-reference extraction, and token resolution.
Changed
package.jsongains thereleasescript. No published file changed.
v0.2.0 - byte-faithful writes and content-hash guards
[0.2.0] - 2026-10-15
Fidelity and guard hardening: a notebook now survives a write untouched except
for the edit you asked for, and every mutating tool can be guarded by a content
hash rather than a timestamp. Ported back from the Python CLI skill that had
grown these properties first.
Added
- Layout-preserving serialization.
lib/nb/style.jsprobes the layout a
notebook already uses — JSON indent (0/1/2/4), CRLF vs LF, and whether the
file ends with a newline — and writes it back in that same layout. Cells whose
sourceis a plain string stay a string; keys that were absent
(outputs,execution_count,metadata) are not invented. A file that is
read and written back unmodified is byte-identical, which makes the plugin
safe to point at notebooks produced by JupyterLab,nbformat.writes, or hand
editing. Known limits: tab indentation,\uXXXX-escaped non-ASCII (Python's
ensure_ascii=True), and more than one trailing newline are normalized to the
tool's defaults. expected_file_sha256on all four mutating tools.expected_file_mtime_ms
cannot see a rewrite that happens inside the same millisecond; the content
hash can.notebook_readandnotebook_list_cellsnow returnfile_sha256
so the value never has to be guessed.expected_source_sha256onnotebook_insert_cell(its anchor cell) and
notebook_clear_outputs(its target cell). Every mutating tool now accepts
the same three guards.- Deterministic ids for notebooks too old to store them. A missing cell id
is derived fromsha256("v1|<ordinal>|<source>")instead of random bytes, so
the same cell gets the same id on every load, across processes. - Structure validation before every write. The save path runs the deep
audit and refuses to write a notebook that fails it — cell ids must match
^[A-Za-z0-9_-]{1,64}$and be unique,metadatamust be an object when
present, a code cell'soutputsmust be an array when present, and every
element of asourcearray must be a string.notebook_validatereports the
same deep problems. test/fidelity.test.js— byte-for-byte round trips across six file layouts,
deterministic id behavior at nbformat 4.4 and 4.5, guard coverage for all four
writers, append-only behavior for an unedited cell, and the documented
non-preservable cases. Suite: 61 → 75 assertions.
Changed
- Writes no longer reformat the whole file. The old save path rebuilt the
document and forced a 1-space indent, which reformatted every notebook it
touched (and silently dropped unknown root keys). nbformat_minoris no longer bumped. Backfilling an id never changes the
declared notebook format.
Fixed
- README and CHANGELOG claimed id backfill "bumps
nbformat_minorto 5". No
code path ever did that; the documentation now matches the implementation. - A notebook that is valid enough to inspect but structurally broken is now
reported bynotebook_validateand refused by the writers, instead of being
silently loaded and rewritten.
v0.1.2 — cell_type self-check in the companion skill
Documentation release: the companion skill now knows about the one failure mode that its own structure checks cannot see.
Added
- A
cell_typeself-check in the style section: the first cell and every## N.heading cell must bemarkdown. Markdown prose typed ascodeis executed as Python and dies on the first non-comment line withSyntaxError: invalid character '(' (U+FF08)— a failure reported several cells away from the cell that actually has the wrong type. - A wrong-type trap in the "after editing" checklist: after any layout refactor, re-check cell types with
notebook_list_cells.notebook_validateonly validates structure, so a markdown body sitting in a code cell passes it and still breaksRun Allon the first cell.
Changed
- The troubleshooting order for "the notebook won't run" is now: check
cell_typefirst, then check for a cell stuck waiting oninput().
No runtime code changed — lib/ is byte-identical to v0.1.1. 61 package tests pass.
v0.1.1 - notebook authoring style in the skill
Codifies the notebook authoring style in the companion skill. This is the layout these tools were originally used to produce — now written down instead of left to the model's habits.
Added
A new 写法约定(风格) section in skill/notebook-tools/SKILL.md:
- Section commentary belongs in its own markdown cell — never as a
# 1. 标题banner inside a code cell. - Each section = 1 markdown cell + 1..n code cells. The markdown cell carries
## N. 中文标题(numbered from 1) plus a sentence or two of prose, and an optional-bullet list. - The first cell carries
# <filename without extension>→ one sentence on what the script does → optional## 使用方法+ ordered list → a single closing---. - Callouts use blockquotes:
> **注意**: …,> ✅ 已完成 …. - Multiple code cells in one section share a single markdown header.
- An explicit user request for a different layout still wins.
Changed
- The skill
descriptionandwhenToUsenow list creating/authoring notebooks as a trigger, not only reading and cleaning them.
Full diff: v0.1.0...v0.1.1
v0.1.0 - initial public release
First release: a DSH port of OpenClaw's openclaw-notebook-tools (MIT).
Nine cell-aware Jupyter notebook tools, zero runtime dependencies, node: builtins only.
Tools
| Tool | Writes | Purpose |
|---|---|---|
notebook_create |
yes | Create a notebook from structured cells |
notebook_read |
no | Structured read, optionally with outputs |
notebook_list_cells |
no | Cheap index: id, type, preview, counts |
notebook_search |
no | Find cells by text or regex |
notebook_validate |
no | Validate structure and report facts |
notebook_edit_cell |
yes | Replace a cell's source / type |
notebook_insert_cell |
yes | Insert a new cell at a position |
notebook_delete_cell |
yes | Delete a cell by id or index |
notebook_clear_outputs |
yes | Clear outputs (all or one cell) |
Semantics
- Atomic saves — 1-space indent, sibling temp file,
fsync,rename. A killed process cannot leave a half-written notebook, and temp files never land in the system temp directory. - Cell ids over indexes — reads always return an
id; missing ids are backfilled in memory and persisted on the next save (bumpingnbformat_minorto 5). - Editing a code cell clears its
outputsandexecution_count. - Stale-edit guards —
expected_source_sha256/expected_file_mtime_msfail before any write; mtime comparison tolerates sub-millisecond jitter. - Source on disk is a string array (one element per line, trailing newline preserved).
- nbformat 4 only. No kernel execution, ever.
Install
dsh plugin --profile desktop add --config.minimumReleaseAge=0 <path-or-tarball>
Then restart DSH: the cordis.patch.yml row is composed at boot. The companion skill lives at skill/notebook-tools/SKILL.md — copy it into <DSH_HOME>\skills\ and DSH hot-reloads it.
Verification
61 node:test assertions plus an end-to-end pass in a live DSH session covering all nine tools, both stale guards, and output clearing on disk.
Credits
Upstream tool surface, behavioral spec, and companion skill by Kittipos Sirivongrungson (Lightbridge-KS/openclaw-notebook-tools, MIT). This port re-implements the behavior for the DSH plugin host. LICENSE carries both copyright notices.