Skip to content

Releases: beihzb/dsh-notebook

v0.4.1 — community fixes: cwd sync, /nb auth, Promise leak, CPU false positive

Choose a tag to compare

@beihzb beihzb released this 04 Nov 11:41

dsh-notebook v0.4.1

Four community fixes (thank you @Enlux) plus the R
kernel work from 0.4.0, in the first build published to npm.

0.4.0 was tagged and released on GitHub but never reached npm; the fixes below
landed before the publish, so 0.4.1 is the first installable build that
contains both.

Session cwd sync no longer kills the kernel (#6)

A freshly mounted notebook view synced the session's cwd with
killSidecar(); await ensureKernel(). Two consequences:

  • every variable was discarded, and a running cell was interrupted, just because
    the view mounted;
  • it fired spuriously, because the session state was created from
    initialWorkspaceRoot — read once at plugin activation — rather than from the
    session's real cwd.

Now: new state is seeded from SessionHeader.cwd, and both the sync and
nb_set_cwd change the directory in place through a new driver chdir
command. That command goes through the sequential shell channel with
store_history=False, so it queues behind any in-flight cell and never touches
the execution counter — the directory changes when the queue drains, and
variables survive.

The PR's snippet was Python-only (import os; os.chdir(...)), which would have
thrown in an R kernel; the shipped version picks setwd() there. Its
cwdHint parameter was also threaded through 28 registerTool call sites — here
it is injected once, in the shared tool wrapper.

/nb routes are authenticated and body-bounded (#3)

The notebook's HTTP routes had no admission control: any local process, or any
website via a cross-origin form POST, could drive the kernel and execute
arbitrary Python. Every route now asks the connection service for a rejection
first — the same Host/Origin + login-cookie fence the platform puts in front of
/api — and readJson caps bodies at 32 MB instead of accumulating without
limit.

No Promise leak into tool output (#5)

The in-flight run Promise was stored on the cell object, so while a cell was
queued or running, every snapshot — and therefore nb_get, nb_edit_cell, a
background nb_run_cell — carried a live Promise. The GUI's JSON.stringify
silently dropped it, but the agent-tool pipeline validates lossless JSON and
rejected it. Jobs now live in a side Map keyed by cell id, and snapshots strip
a stray _runJob if one ever appears again.

No false hybrid-CPU warning on servers (#4)

The Linux fallback guessed "hybrid" from a cpu MHz max−min gap above 300. That
is true of every frequency-scaling CPU: an idle EPYC/Xeon core sits below
1.5 GHz while a busy sibling boosts past 3 GHz, so homogeneous servers showed the
P+E advisory. Only an explicit per-core core_type marker counts now; a CPU
that does not expose it reports no hybrid topology.

Verification

  • tmp/verify-chdir.py — starts a real Python kernel, defines a variable, runs
    chdir, then asserts the directory moved and the variable survived (proof
    it was not a restart) and the execution counter was untouched.
  • tmp/verify-r-kernel.py — extended with the R branch of chdir (setwd), plus
    exists("x") still true afterwards.
  • tmp/verify-ipynb-io.py (52), tmp/verify-0.4.0.mjs, tmp/verify-0.3.1.mjs
    — unchanged and still green after the merges.

v0.4.0 — R kernels, no nbformat, reopenable CPU hint

Choose a tag to compare

@beihzb beihzb released this 31 Oct 09:09

dsh-notebook v0.4.0

R kernels, a notebook format that no longer needs nbformat, and a hybrid-CPU
hint you can actually find again.

R kernels

IRkernel is now a first-class kernel, discovered the way Jupyter discovers it:

  • kernelspecs in the user data dir (%APPDATA%\jupyter\kernels,
    ~/.local/share/jupyter/kernels),
  • kernelspecs shipped inside a conda env (<env>/share/jupyter/kernels),
  • conda environments that merely have IRkernel in their R library — a
    kernelspec is synthesised for them.

Two details that make it work in practice:

  • A conda R needs its env's DLL directories. Launching the bare
    <env>/lib/R/bin/x64/R.exe exits instantly with 0xC0000135 (DLL not found).
    The kernel is started with the env's bin/Library/bin directories prepended
    to PATH and R_HOME set — the equivalent of conda activate, without a
    shell.
  • argv[0] is not trusted. The kernelspec a conda env ships usually says
    "R", which only resolves while that env is activated (and often lacks the
    .exe). The absolute binary is substituted, and the spec is regenerated in a
    temp dir exposed through JUPYTER_PATH, so your Jupyter config is untouched
    and nothing has to be installed.

nb_list_vars / nb_inspect_object have R implementations (ls/class/
deparse; data.frame additionally reports rows, columns and column types).
Saving writes the real kernelspec.language (r or python).

Every entry in the kernel dropdown is labelled with its language — py · scRNAv2, R · ggtree — with the full launch line as its tooltip.
nb_kernel_list reports the same language field, and the catalog is projected
before it leaves the host, so an R entry's argv and activation PATH/R_HOME
stay on the host instead of reaching the browser or the agent's context.

R kernels are opt-in: a fresh notebook still defaults to Python. Pick one in
the kernel dropdown (R · ggtree, R · hdWGCNA, …). An env that has Python and
R together is reported to the agent by dsh-envsel 0.3.1, which now states the
Rscript of the selected python environment.

nbformat is no longer a dependency

Opening a notebook used to import nbformat inside the sidecar, and the sidecar
runs on the interpreter you selected. Any conda env with ipykernel +
jupyter_client but without nbformat therefore failed on open with:

Operation failed: No module named 'nbformat'

That is a common shape — nbformat is not an ipykernel dependency — and a lean
or R-only environment can never have it. Reading and writing .ipynb now uses
the standard library and is byte-identical to nbformat.write(): same
indent=1 / sort_keys / ensure_ascii=False, same line-list form for
multi-line strings, same rule that only text/* mime payloads are split,
image/png base64 kept as one string, same CRLF and trailing newline. Files do
not churn in git.

jupyter_client and ipykernel remain genuine requirements; a missing one is
now reported as such (and the kernel dropdown marks the env unavailable) instead
of surfacing a raw ModuleNotFoundError.

Lossless notebook round-trip

Anything the plugin does not model survives a load → save cycle: markdown
attachments (pasted images), per-output metadata (widget view sizes),
metadata.widgets state, vendor keys. Outputs the file format does not allow
(update_display_data) collapse to display_data; duplicate or missing cell ids
are regenerated so the result always passes nbformat.validate.

nbformat 3 notebooks — worksheets / input / prompt_number and the
png/html/json mime aliases — are upgraded on read (mirroring
nbformat.v4.convert) instead of being read as an empty notebook and wiped on
the next save. 4.0–4.4 files gain stable cell ids exactly as
nbformat.read(as_version=4) does, with orig_nbformat_minor recorded.
A document that is neither v4 nor v3-shaped is rejected instead of being treated
as empty.

The hybrid-CPU hint can be reopened

Dismissing it used to be permanent for the browser session, with no entry point
anywhere. The CPU probe result now stays in the session and a
⚡ 大小核 / ⚡ Hybrid CPU toolbar button reopens the same panel — only the
automatic popup remains dismissed.

Verification

  • tmp/verify-ipynb-io.py — 52 checks: byte-identity with nbformat.write,
    nbformat.validate acceptance, attachments/widgets/application/json/
    update_display_data round-trip, v3 upgrade, 4.4 → 4.5, malformed input.
  • tmp/verify-sidecar-no-nbformat.py — drives the real sidecar with an
    interpreter that genuinely lacks nbformat and loads/saves a notebook.
  • tmp/verify-r-kernel.py — 19 checks through the real sidecar: start, execute,
    error path, list_vars, inspect_object on a data.frame, restart.
  • tmp/verify-0.4.0.mjs — extracts the shipped discovery functions and runs them
    against the real machine (R kernels resolve to real binaries with a valid
    R_HOME; envs without IRkernel are not offered; one R binary is not listed
    twice).

Compatibility

Unchanged: @deepseek-ai/dsh-tools: ^0.1.0-rc.6 || ^0.2.0-rc.1, verified against
DSH 0.2.0-rc.2. Existing notebooks, tools and UI are unaffected.

v0.3.1

Choose a tag to compare

@beihzb beihzb released this 10 Oct 07:54

v0.3.1

The default kernel now follows the current session's dsh-envsel selection, and a session with no selection of its own — every delegated subagent — inherits the nearest selecting session it descends from.

The bug

defaultKernelEntry() resolved the envsel environment through envselPython(), which had two problems:

  1. It never found the session. The function read process.env.DSH_ENV_PYTHON first, then fell back to reading ~/.dsh/envsel-state.json and taking the last entry in the file, ignoring the session entirely:

    const selections = Object.values((doc && doc.selections) || {})
    for (let i = selections.length - 1; i >= 0; i--) { … }   // ← whichever session was appended last

    The store is keyed by session id and appended in creation order, so "last" means the most recently created session that picked anything — an unrelated session. Pick an environment in one new session, and every notebook's default kernel followed it.

  2. process.env.DSH_ENV_PYTHON can never be set. The harness resolves DSH_ENV_* through the shellEnv registry per shell execution and passes the result to that one child process; it never writes them into the DSH host process. So that first branch was dead, and the file fallback was the only path taken.

A delegated subagent fared worse than "wrong": DSH gives every subagent its own session (origin: "subagent", delegationDepth: 1), so it matched nothing at all and silently got the first ipykernel-capable environment.

The fix

  • Resolve per session, then by lineage. The interpreter is resolved for the addressed session: its own envsel-state.json entry first, then the nearest entry owned by a session it descends from, recorded from exec.agent.session.header.parentSession on each notebook tool call. The walk is bounded (8 hops), breaks on a repeated id, and yields null when nothing matches — in which case the plugin falls back to the first ipykernel-capable environment as before.
  • Version-checked store read. The document's version must be 1; an unrecognized shape is ignored rather than misread, since this plugin does not own that schema.
  • $DSH_HOME is honoured instead of a hard-coded ~/.dsh.
  • Short read memo (2 s) so status polling does not hit the disk on every snapshot.
  • Every per-session entry point is now session-aware: the sidecar spawn (pythonExe), kernel start (ensureKernel) and the public snapshot. The session-independent CPU probe keeps its old behaviour deliberately.

Compatibility

No manifest change: peerDependencies still read @deepseek-ai/dsh-tools: ^0.1.0-rc.6 || ^0.2.0-rc.1, and the client bundle is untouched. This release is host-side only (lib/index.js).

dsh-envsel remains optional. Pair it with @beihaizb/dsh-envsel@0.3.0, which is where the selection also starts reaching the model as text; this release is what makes the notebook kernel agree with it.

Verification

tmp/verify-0.3.1.mjs (no browser, no DSH needed) extracts the actual shipped functions from lib/index.js and runs them in a controlled sandbox — 25 checks, all passing:

  • source assertions: session-aware entry points present, the "last entry" loop gone, version stamp checked, DSH_HOME honoured, the session-independent probe preserved;
  • behaviour: own entry wins; immediate parent inherited; unselected intermediates skipped; nearest ancestor wins; empty lineage returns null; a foreign store version returns null; a missing interpreter file is not adopted; inheritance works within the hop bound and stops past it; a lineage cycle terminates; the read memo expires after its TTL.

Install

dsh plugin --profile web add @beihaizb/dsh-notebook@0.3.1

Then restart dsh web.

Version pinning: if you install shortly after this release, pnpm's supply-chain policy (minimumReleaseAge) may skip the just-published version and install an older one. Pin the version explicitly (@beihaizb/dsh-notebook@0.3.1), or use @latest once the release is older than the policy window.

v0.3.0

Choose a tag to compare

@beihzb beihzb released this 09 Oct 10:47

v0.3.0

Long-output folding that actually works, oversized output rendered head+tail, Notebook reachable in a brand-new session, scroll position that survives View switches — plus DSH 0.2.0 compatibility.

Compatibility (important)

DSH refuses to boot a plugin whose @deepseek-ai/dsh* peer ranges exclude the running runtime. The gate lives in @deepseek-ai/dsh-app-boot (evaluatePluginCompatibility, which throws at boot) and @deepseek-ai/dsh-plugin-manager (incompatible-version), and it only inspects @deepseek-ai/dsh / @deepseek-ai/dsh-* peers.

v0.2.4 declared @deepseek-ai/dsh-tools: ^0.1.0-rc.6, whose upper bound is <0.2.0 — so on a 0.2.x runtime the whole bundle was skipped at boot (the plugin stays installed but profile startup denies it). It now declares:

"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1"

which satisfies 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2 and 0.2.1-alpha.2. Everything else in this plugin's contract was verified unchanged between 0.1.7-rc.2 and 0.2.0-rc.2: dsh-tools, dsh-host-webserver, dsh-sandbox-policy, dsh-client-ui-slots, dsh-client-modules, dsh-plugin-manager, dsh-web-app and dsh-agent are byte-identical, and the conversation.view slot, ctx.slots.inject/register, dsh.bundle.patch, defineTool/ctx.tools.register, webServer.register({kind:"prefix"}) and session/disposed are all unchanged. No manifest/API version gate exists — the peer range is the only one.

The client manifest also stopped naming @deepseek-ai/dsh-client-runtime (absent from npm at 0.2.x) and @deepseek-ai/dsh-client-ui-slots (no dsh.client row), pointing instead at @deepseek-ai/dsh-client-ui-renderer — the client row that actually provides the slots service. That key is advisory load ordering only (the loader silently skips unknown rows), so this is cleanup, not a fix.

Fixes

  • Folding never triggered (the v0.2.2 feature was dead code): OutputView gated the fold class on scrollHeight > clientHeight + 8 measured on the very element that the fold class clamps. Without the clamp the two values are equal, so overflow was never detected: the button never rendered and no output was ever folded. The measurement now runs against an inner, unclamped element and compares against a single FOLD_PX constant shared with the CSS clamp.
  • Scroll position reset when switching Views: the shell renders one View at a time, so switching to 对话 unmounted the notebook and destroyed its scroll container, resetting the position to the top. The offset is now remembered per session (and restored once the cell list is tall enough to hold it, with a bounded retry while the state loads).
  • Folded/expanded choices survived nothing: an expanded cell re-folded on every View switch. Each cell's choice is now remembered too.

Features

  • Long-output folding with a real affordance: the button reports the hidden size ("Expand all (3,000 lines)"), and the collapsed box keeps its gradient cue.
  • Oversized output renders head+tail: one text block above 4,000 lines or 200 KB renders its first 300 and last 100 lines with an explicit "… N lines omitted" marker plus a one-click "Show all N lines". A 500 KB stream no longer has to live in the DOM just to be folded.
  • Notebook reachable in a blank session: DSH omits the conversation.view slot while a Session is blank (the view area returns null and the View tabs are hidden), so the Notebook tab used to require sending a message first. The plugin now also registers a uiConversation View definition (so its target can count as conversation activity) and offers a "📓 Open Notebook in this session" entry above the composer while the session is blank; activating the target makes the shell treat the session as started, which reveals the normal View tabs. The entry renders only while the session is blank, and the whole path is guarded — on a runtime without uiConversation the plugin behaves exactly as before.

Install

dsh plugin --profile web add @beihaizb/dsh-notebook@0.3.0

Then restart dsh web. A Notebook tab appears at the top of the session.

Version pinning: if you install shortly after this release, pnpm's supply-chain policy (minimumReleaseAge) may skip the just-published version and install an older one. Pin the version explicitly (@beihaizb/dsh-notebook@0.3.0), or use @latest once the release is older than the policy window.

v0.2.4

Choose a tag to compare

@beihzb beihzb released this 26 Aug 01:56

v0.2.4

ANSI escape codes no longer leak into the notebook output area (fixes #2).

Version note: the first attempt at this fix was tagged v0.2.3, but that version could not be published to npm (npm forbids re-publishing an unpublished version number). This release carries the identical fix under v0.2.4.

Fixes

  • ANSI stream output rendered verbatim (issue #2): kernel stream text containing ANSI escape sequences without \r (e.g. \x1b[32m...\x1b[0m) was rendered raw — the user saw literal [32m / [0m garbage. Root cause: OutputView's stream branch already computed the stripped string via renderStreamText, but the render line (r && r.tail) || o.text discarded it whenever r was a string (no \r) and fell back to the raw o.text. The stripped result is now used for string results; \r-style streams (tqdm progress) are unchanged.
    • Real-world impact: IPython's colored Pdb (ipdb> prompt, source lines wrapped in \x1b[32m...) and colorama-style colored prints rendered as [32m... garbage.
  • ANSI in error titles (evalue): TracebackView stripped ANSI from traceback lines but not from the error message itself (ValueError: \x1b[31m...), so colored exception messages leaked the same way. Now stripped too.
  • \r stream with an empty last segment: a stream ending in a bare \r (line cleared) previously fell back to the raw text; it now renders as empty, which is the correct visual state.

Install

dsh plugin --profile web add @beihaizb/dsh-notebook@0.2.4

Then restart dsh web. A Notebook tab appears at the top of the session.

Version pinning: if you install shortly after this release, pnpm's supply-chain policy (minimumReleaseAge) may skip the just-published version and install an older one. Pin the version explicitly (@beihaizb/dsh-notebook@0.2.4), or use @latest once the release is older than the policy window.

v0.2.2

Choose a tag to compare

@beihzb beihzb released this 21 Aug 17:07

v0.2.2

Structured execution results for the agent, context-safe long output, and several execution-loop reliability fixes.

Structured execution results (agent execution loop)

  • Canonical execution envelope: nb_run_cell, nb_edit_and_run_cell, and nb_run_all now return a uniform envelope — status, execution_count, duration_ms, kernel_state, merged stdout/stderr, outputs_summary, image paths, error + traceback_text, and execution_index — instead of raw output arrays the agent had to parse.
  • Context-safe long output (3 layers):
    1. Per-stream driver cap (500 KB) protecting stdio / host memory / UI;
    2. 6 KB head+tail summary in the envelope (tail carries tracebacks / latest output) with an explicit omission marker;
    3. The full merged text is spilled to <notebook>_files/<cell>_stdout.txt and the envelope carries stdout_path / stderr_path, so the agent reads details on demand — long output can never blow up the context.
  • Images out of context: display figures are written to <notebook>_files/ and the envelope returns their paths; base64 stays in cell.outputs for the UI only.
  • Per-execution history: every run is recorded in cell.metadata.dsh.executions (capped at 50 per cell, persisted with the notebook) — the foundation for the execution-history UI and replay-based recovery (#3 / #6 of the roadmap).

Fixes

  • Run All "completion" toast was a lie: the "All cells run" toast fired the instant the request returned, while cells were still executing. It now fires only when every cell has reached a terminal state (ok/error).
  • Queue state not shown / editor flashing blank: the HTTP run endpoint had been made to block on wait for the UI, which (a) delayed the Queued state on a second cell until the first finished, and (b) let a compact snapshot (no source) overwrite the editor — cells flashed empty then recovered on the next poll. The UI contract (immediate {queued:true} + full snapshot) is restored; agents still get the full envelope via explicit wait:true.
  • t is not a function render crash: a local var t (poll timer) shadowed the i18n translator inside the new completion-detection effect; renamed to pollTimer. (Same class of bug as the v0.2.1 "Hand to AI" fix.)
  • Long output >6 KB but <500 KB had no spill file: the spill-on-demand path only triggered on driver-level truncation; it now also triggers when the 6 KB envelope summary is cut, so the agent always has a path to the full text.
  • Re-running an already-queued cell returned a stub: nb_run_cell on a cell already queued/running now waits for the in-flight job and returns the real envelope instead of an immediate non-envelope stub.

Install

dsh plugin --profile web add @beihaizb/dsh-notebook@0.2.2

Then restart dsh web. A Notebook tab appears at the top of the session.

Version pinning: if you install shortly after this release, pnpm's supply-chain policy (minimumReleaseAge) may skip the just-published version and install an older one. Pin the version explicitly (@beihaizb/dsh-notebook@0.2.2), or use @latest once the release is older than the policy window.

v0.2.1

Choose a tag to compare

@beihzb beihzb released this 21 Aug 15:57

v0.2.1

Bug-fix + polish release.

Fixes

  • Plugin now loads when installed as @beihaizb/dsh-notebook (#1)
    • cordis.patch.yml referenced the unscoped dsh-notebook, which could not resolve against the scoped npm package, taking down the whole plugin tree (Failed to load plugins).
    • Both the host patch name and the client self-register id now use @beihaizb/dsh-notebook, matching @beihaizb/dsh-envsel's working setup.
  • "Hand to AI" now actually reaches the agent
    • A local variable (var t) shadowed the i18n translator t() after the v0.2.0 i18n refactor, throwing TypeError before the draft was submitted — the AI never received the cell code or request. Renamed the local to txt.

Improvements

  • Theme-aware cell editor: the CodeMirror editor follows the DSH light/dark theme — oneDark in dark mode, a clean light scheme with soft-gray line numbers in light mode — and re-themes live (MutationObserver) when you switch themes.
  • Localized CPU-hint snippet: the copy-ready psutil snippet's comments now follow the UI language (EN / 中文) instead of being hardcoded Chinese.
  • @codemirror/language added as an explicit dependency for the light-mode syntax highlighting.

Install

dsh plugin --profile web add @beihaizb/dsh-notebook

Then restart dsh web. A Notebook tab appears at the top of the session.

v0.2.0 - Per-Session Isolation, Kernel Manager, Parent Watchdog, Bilingual UI

Choose a tag to compare

@beihzb beihzb released this 21 Aug 01:58

What's New in v0.2.0

Per-Session Isolation

Each DSH session now owns an independent notebook, kernel, and working directory. Multiple sessions can run kernels simultaneously without state leakage.

Kernel Manager

A new toolbar popup lists all active kernels across sessions — session ID, kernel name, and status. Close any session's kernel directly from the manager.

Kernel Activity Indicator

A status bar shows whether the current session's kernel is active and warns when multiple sessions have kernels running (memory / resource awareness).

Parent Watchdog (Orphan Cleanup)

If dsh web is force-killed, the Python sidecar detects parent-process death and automatically shuts down its ipykernel — no orphan processes left behind.

  • POSIX (Linux/macOS): getppid() reparent detection
  • Windows: ctypes.OpenProcess query (NOT os.kill, which kills on Windows)

Bilingual UI (EN / 中文)

The entire interface now defaults to English with a one-click language toggle in the toolbar. Your language choice persists across sessions via localStorage.

Bug Fix

Fixed a critical crash where the parent watchdog's os.kill(parent, 0) on Windows called TerminateProcess and killed the dsh web main process ~2 seconds after any cell execution. Replaced with ctypes.OpenProcess(SYNCHRONIZE, ...) which queries without killing.


Full Changelog: v0.1.3...v0.2.0

v0.1.3

Choose a tag to compare

@beihzb beihzb released this 20 Aug 12:23

v0.1.3 — Performance, resilience, session cwd sync, hybrid-CPU hint

Performance

  • Live progress events are throttled and compacted (only the last few outputs, stream tails) instead of resending the full output set every ~80ms.
  • Browser state polling reduced (1000ms while running / 2500ms idle); duplicated React state updates removed.
  • Frequent /nb/get and run responses no longer resend the full kernel list (kept only where needed; the UI preserves it).

Resilience

  • Fixed a React hooks-order crash in the output view that could blank the whole Notebook panel after a cell run.
  • Added a lightweight error boundary so a single bad output shows a recoverable message instead of an empty panel.

Session-aware workspace

  • Notebook default cwd/workspace now follows the current DSH session instead of the dsh web process directory.
  • Manual "set working directory" and opened-notebook cwd take priority over auto-sync.

Hybrid-CPU (big.LITTLE) hint

  • Detects hybrid CPUs cross-platform (Windows EfficiencyClass, macOS perf levels, Linux core_type/frequency gap) by physical topology — no CPU-name whitelist, so future chips are recognized.
  • One-time per-session hint (dismissible, session-scoped) with copy-ready psutil snippet that pins the Python process to P-cores and raises priority.
  • Fixes a previous snippet that only raised priority without actually setting CPU affinity.

Docs

  • Updated English + Chinese README with the new features.

v0.1.2

Choose a tag to compare

@beihzb beihzb released this 20 Aug 07:32

What's Changed

  • Add agent runtime introspection tools:
    • nb_context
    • nb_list_vars
    • nb_inspect_object
  • Add pandas DataFrame summaries for AI agents:
    • shape, columns, dtypes, missing counts, head, numeric summary, categorical summary, memory usage, and index metadata.
  • Add safe AI edit-and-rerun workflow:
    • nb_edit_and_run_cell
    • richer per-cell version snapshots
    • nb_cell_history
    • nb_revert_cell
  • Add error repair-loop foundation:
    • nb_error_context returns traceback, source, nearby cells, and live variables.
  • Update README / README.zh-CN tool lists and roadmap.

Privacy

Release package contents were scanned before publication preparation. The npm tarball contents are limited to the plugin source, README files, license, patch metadata, and notebook skill file; local notebooks, temporary tests, and git logs are not included.