Skip to content

Commit 6fa9b9b

Browse files
pankgeorgclaude
andauthored
feat: night-shift roadmap — streamable HTTP, cell sync, terminal interrupt, review fixes
* test: add platformUtils unit tests * docs: refresh MCP guide for current tools, add pluto-cli skill - PLUTO_GUIDE.md's workflow section now teaches wait_for_notebook_idle (instead of read_cell/list_cells polling loops), the 5-minute timed_out semantics of execution tools, adoption of already-open notebooks, and export_notebook_html - new .claude/skills/pluto-cli skill teaches the CLI validation workflow to Claude Code sessions in this repo * feat: speak streamable HTTP (modern MCP transport) alongside legacy SSE - POST /mcp handles streamable HTTP sessions (initialize creates one, mcp-session-id routes follow-ups); GET /mcp with a session header serves the streamable event stream, plain GET stays legacy SSE, and DELETE /mcp ends a session — old configs and the CLI's own client keep working unchanged - install and the extension's config commands write type 'http' again, now that the server actually speaks it; /health reports session counts per transport - verified live: SDK StreamableHTTPClientTransport lists all 23 tools and calls get_notebook_status; legacy 'cli tools' works in parallel * feat: sync external cell adds/removes/reorders into the VSCode view Implements the long-stubbed _handleCellReorder. Any cell_order patch (add/remove/replace) schedules a coalesced sync that rebuilds the VSCode cell list from Pluto's state: cells present in both keep their VSCode content and outputs, cells created via MCP tools or Pluto's browser UI are materialized (markdown unwrapping, code_folded and pluto_cell_id metadata), and remotely deleted cells disappear. Feedback-loop protection: a per-notebook suppression counter makes handleVsCodeNotebookChange ignore the change events our own applyEdit produces (released on the next tick), and the sync defers while a local cell add is mid-round-trip (a cell without pluto_cell_id would otherwise be destroyed by the rebuild). Needs manual verification in the Extension Development Host — CI covers types/lint/units only. * feat: terminal Ctrl+C interrupts the running Julia computation Previously Ctrl+C only reclaimed the terminal prompt; the Julia-side computation kept running. Now it also sends worker.interrupt() (best-effort) so the kernel actually stops. * test: real integration tests, wired into CI via xvfb Replaces the scaffold sample with tests that exercise the extension host: activation, command registration, and opening a real .pluto.jl file through the serializer (type, cell parsing, metadata). New Integration Test CI job runs them under xvfb-run — this is the check that was commented out with a TODO since the beginning. * chore: npm audit fix — resolve both critical advisories Semver-compatible bumps only (no --force): 45 -> 23 advisories, the remaining ones live inside dev tooling (vsce/npm internals) and need major bumps there. Runtime deps ws and @modelcontextprotocol/sdk are now clean; full suite, both builds, and CLI smoke verified. * docs: bond-status findings, upstream rainbow notes, README consistency - README Dev TODO documents exactly what works and what's stubbed in the PlutoUI bond path (set_bond round trip works; state distribution and js-link interop need controller→renderer plumbing + manual UI verification) - docs/UPSTREAM_RAINBOW_NOTES.md: paste-ready upstream issue drafts for the waitSnippet listener race and the nbpkg serialize round-trip gap, each with cause analysis and our local workaround - renderer: bond acks no longer logged as unknown messages - README: Claude Desktop → Claude Code (matches the relabeled command) * chore(deps): bump all deps to latest, pin Julia at 1.12.6 Runtime: @modelcontextprotocol/sdk 1.30.0, uuid 14.0.1, ws 8.21.3, zod 4.4.3 (rainbow already latest at 0.6.21). Dev tooling to latest compatible; npm audit 23 -> 11, all remaining inside dev tooling. Held back: eslint 10 / typescript 7 (typescript-eslint v8 peer range), @types/vscode (must match engines ^1.75.0), @types/node (extension host is Node 22). juliaup pin 1.11.7 -> 1.12.6 (CLI default, docs, config example); CI now sets up Julia 1.12. Three files reformatted by prettier 3.9. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: react to all Pluto updates, stop autosave echo duplicating cells Execution lifecycle: executions now end on the running:false patch (as the design intended) instead of on the first output patch, which ended cells prematurely — 'using PlutoUI' reported success while nbpkg was still installing. Streaming output while running no longer closes the execution, and a late duplicate output patch can no longer clobber the displayed duration with a ~0s synthetic execution (per-cell last_run_timestamp guard). Patch-less update events (cells_updated after a reactive re-run, cell_local_update) carry the full notebook state but were dropped as UNHANDLED — stale errors survived a successful re-run and the tree view never refreshed. They now reconcile every settled cell whose result changed since it was last rendered. Cell duplication: Pluto autosaves the notebook file while VSCode has it open; the resulting document revert fired contentChanges that were re-applied to Pluto as user edits, minting new UUIDs for cells Pluto already had (three copies of 'using PlutoUI' per Run All). Added cells whose id Pluto already knows are skipped as echoes, cells with unknown ids keep their identity through addSnippet, and removals whose id still exists in the document are ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: add release workflow docs, license placeholder, test notebooks LICENSE currently reads 'Not licensed yet (bear with us!)' which contradicts the MIT declared in both package.jsons — committed as-is, reconcile before publishing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: sync Pluto-side code edits into VSCode cell text cell_inputs.<id>.code replace patches (browser UI edits, MCP update_cell) were logged UNHANDLED — the switch case listened for 'cell_input' (singular) which never matches. The patch now rewrites the matching cell's text, unwrapping md""" wrappers for markup cells. Echoes of our own executeCell push are dropped by text equality, and the remote-edit suppression (now shared via beginRemoteEdit/ endRemoteEditSoon) guards the applyEdit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style: prettier formatting for controller, test notebook state Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: release workflow runs in with-commit mode, document secret requirements Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: address review findings in controller execution and cell sync - finishExecution renders the final state before ending, so a running:false patch ordered before the output patch in one batch can no longer strand the run-start placeholder (stamp dedupe would have discarded the later output patch) - pasted cells carry the source cell's pluto_cell_id; when the id maps to more than one document cell the copy gets a fresh identity instead of silently aliasing (and overwriting) the original Pluto cell - drag-reorders surface as remove+add of the same id; they now route to moveSnippets instead of being swallowed by the echo guards and later reverted by the order sync - state-reset path guard was '!queued || !running' (always true), ending mid-run executions with stale results - deferred order syncs reschedule with backoff and re-fire once a cell gains its id, instead of dropping remote structural changes forever; sync also defers while executions are in flight so replaceCells cannot orphan them - notebook close clears rendered-output stamps and re-subscribing reconciles from worker state, so reopening a notebook with a live worker renders existing results instead of staying blank - remote cells materialize via createVsCodeCellFromPlutoCell (keeps metadata and outputs) instead of a drifted hand-rolled copy Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): 404 for unknown streamable sessions, sweep idle sessions Unknown session ids on POST /mcp returned 400/-32000, so restarted clients could not tell 'session expired, re-initialize' from a bad request; now 404/-32001 per spec, matching the GET/DELETE handler. Streamable sessions were only removed on explicit DELETE — clients that crash leak a transport+McpServer pair each; sessions idle past 2h are now swept every 10 minutes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(terminal): ctrl-c while queued no longer interrupts the whole notebook Pluto only offers a notebook-wide interrupt; firing it while the terminal snippet was still waiting in waitForIdle killed unrelated editor executions. The interrupt is now sent only while the snippet is actually running — a queued snippet is cancelled by the generation bump alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): integration tests were compiled with noEmit and never ran tsc -p . --outDir out emitted nothing (tsconfig sets noEmit:true, which --outDir does not override), so the vscode-test runner found zero test files and exited 0 — the Integration Test job has been green without executing a single test. compile-tests now uses tsconfig.tests.json (noEmit off, rewriteRelativeImportExtensions for the .ts-extension imports). Suite verified locally: 3 passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: remove duplicate releaserc, fix phantom no-commit references .releaserc.with-commit.json was byte-identical to .releaserc.json, and both docs described a mode switch via a .releaserc.no-commit.json that never existed. Mode switching is now documented as adding/removing the @semantic-release/git plugin block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(release): prepare 0.3.0 (extension) and 0.6.0 (cli) Versions bumped ahead of the squash-merge; CHANGELOG entry written manually. @plutojl/cli npm publishing is switched to manual (npmPublish: false) — the mcp-release workflow still tags and creates the GitHub release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 9235840 commit 6fa9b9b

32 files changed

Lines changed: 12558 additions & 10653 deletions

.claude/skills/pluto-cli/SKILL.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
name: pluto-cli
3+
description: Drive Julia Pluto notebooks from the terminal with npx @plutojl/cli — start a Pluto server, open notebooks, create/edit/execute cells, validate outputs, and export HTML. Use when asked to run, build, validate, or fix a Pluto notebook (.pluto.jl), or to execute Julia code in a reactive notebook context.
4+
---
5+
6+
# Driving Pluto notebooks with @plutojl/cli
7+
8+
The CLI has four subcommands: `run` (start Pluto + the tool server), `tools` (list notebook tools), `call <tool> [json]` (invoke one), and `install` (write MCP config for AI assistants). You mostly need `run` once, then `call` for everything.
9+
10+
## Start the servers (once)
11+
12+
```bash
13+
npx @plutojl/cli run # starts Pluto + tool server on :3100
14+
# or, if a Pluto server is already running:
15+
npx @plutojl/cli run --pluto-url http://localhost:1234
16+
```
17+
18+
Run this in the background — it stays up. First run installs and precompiles Pluto: **allow up to 10 minutes**; the tool server's `/health` endpoint responds throughout. Check readiness with:
19+
20+
```bash
21+
curl -s http://localhost:3100/health # {"status":"ok","plutoServerRunning":true,...}
22+
```
23+
24+
## The validation loop (the main workflow)
25+
26+
Hand-write or edit the `.pluto.jl` file BEFORE opening it (fastest for multi-cell authoring — see `learn_pluto_basics` for the exact file format), then use the notebook as your correctness oracle:
27+
28+
```bash
29+
npx @plutojl/cli call open_notebook '{"path": "/abs/path/nb.pluto.jl"}'
30+
npx @plutojl/cli call wait_for_notebook_idle '{"path": "/abs/path/nb.pluto.jl"}'
31+
npx @plutojl/cli call list_cells '{"path": "/abs/path/nb.pluto.jl"}'
32+
```
33+
34+
- `open_notebook` parses, instantiates, and runs every cell. If the notebook is already open (e.g. the user's browser tab), you attach to that session.
35+
- `wait_for_notebook_idle` blocks until nothing is running or queued. **Use it instead of polling `list_cells` in a loop.**
36+
- `list_cells` shows `errored`/`running`/`queued` per cell — find the failing cell, then `read_cell` for its output, `edit_cell` to fix, and repeat.
37+
38+
## Rules that save you from confusing failures
39+
40+
- **One definition per variable across the whole notebook** (Pluto reactivity). "Multiple definitions" → `list_cells`, find the duplicate, `delete_cell`.
41+
- Execution tools (`create_cell`, `execute_cell`, `execute_code`) return after 5 minutes with `timed_out: true` if the cell is still computing — the computation continues; follow with `wait_for_notebook_idle`. **Never retry a timed-out `create_cell`** — the cell exists.
42+
- Notebooks are **not auto-saved**: call `save_notebook` to persist. Never edit the `.pluto.jl` on disk while it's open — Pluto owns the file.
43+
- Prefer plain `using PackageName` — Pluto installs packages automatically.
44+
- Long/slow cells: `edit_cell` with `run: false`, then `execute_cell`, then `wait_for_notebook_idle`.
45+
- Passing large code through `call`'s JSON argument fights shell escaping — for multi-line cells, write the `.jl` file and `open_notebook`, or pipe carefully quoted JSON.
46+
- `call --timeout <seconds>` raises the client-side wait (default 120s); `--raw` prints the raw JSON response.
47+
48+
## Finishing
49+
50+
```bash
51+
npx @plutojl/cli call save_notebook '{"path": "/abs/path/nb.pluto.jl"}'
52+
npx @plutojl/cli call export_notebook_html '{"path": "/abs/path/nb.pluto.jl"}'
53+
npx @plutojl/cli call get_notebook_url '{"path": "/abs/path/nb.pluto.jl"}' # browser link for the user
54+
```
55+
56+
For the full tool list with parameters: `npx @plutojl/cli tools`. For Pluto file format, reactivity, PlutoUI, and beautification guidance: `call learn_pluto_basics` (or read `src/PLUTO_GUIDE.md` in this repo).

.github/workflows/ci.yml

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
- name: Setup Julia
7878
uses: julia-actions/setup-julia@v2
7979
with:
80-
version: "1.11"
80+
version: "1.12"
8181

8282
- name: Cache Julia packages
8383
uses: julia-actions/cache@v2
@@ -90,9 +90,28 @@ jobs:
9090

9191
- name: Run unit tests
9292
run: npm run test:unit
93-
# //TODO: figureout how to run these in CI and what actually to test there
94-
# - name: Run vscode tests
95-
# run: npm test:unit
93+
94+
test-integration:
95+
name: Integration Test
96+
runs-on: ubuntu-latest
97+
needs: validate
98+
timeout-minutes: 20
99+
100+
steps:
101+
- name: Checkout code
102+
uses: actions/checkout@v5
103+
104+
- name: Setup Node.js
105+
uses: actions/setup-node@v5
106+
with:
107+
node-version: "22"
108+
cache: "npm"
109+
110+
- name: Install dependencies
111+
run: npm ci
112+
113+
- name: Run VSCode integration tests
114+
run: xvfb-run -a npm test
96115

97116
build:
98117
name: Build

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
## [0.3.0](https://github.com/JuliaPluto/advanced-vscode-extension/compare/v0.2.7...v0.3.0) (2026-08-15)
2+
3+
### Features
4+
5+
- **mcp:** streamable HTTP (modern MCP transport) served natively alongside legacy SSE; `install` and the extension config commands write `type: "http"`; `/health` reports per-transport session counts
6+
- **notebook:** cell-order sync — cells added, deleted, or reordered via MCP tools or Pluto's browser UI now appear in the VSCode notebook view
7+
- **notebook:** Pluto-side code edits (browser UI, MCP `update_cell`) sync into VSCode cell text
8+
- **terminal:** Ctrl+C interrupts the running Julia computation (`worker.interrupt()`), not just the prompt
9+
- **mcp:** `PLUTO_GUIDE.md` rewritten around `wait_for_notebook_idle`, session adoption, and HTML export; new `pluto-cli` skill for Claude Code sessions
10+
- **cli:** `@plutojl/cli` 0.6.0 with the same transport and server improvements (npm publish is manual)
11+
12+
### Bug Fixes
13+
14+
- executions end when the cell settles (`running: false`), not on the first output patch — no more premature "success" while nbpkg installs, stale errors clear after a fixing re-run, and displayed durations are no longer reset to 0s by duplicate patches
15+
- Pluto's autosave of an open notebook no longer duplicates cells through document-revert echoes; pasted cells get their own identity instead of aliasing the original; drag-reorders propagate to Pluto instead of snapping back
16+
- reopening a notebook whose worker stayed alive renders existing outputs instead of staying blank
17+
- unknown streamable MCP sessions return spec-mandated 404 so restarted clients re-initialize; idle sessions are swept after 2h
18+
- terminal Ctrl+C while queued no longer interrupts the whole notebook
19+
- the Integration Test CI job compiled with `noEmit` and had been passing while executing zero tests; it now runs the suite
20+
- reactive dependency tracking: bulk `cells_updated` events (no patches) reconcile all changed cell outputs and refresh the notebook tree
21+
22+
### Maintenance
23+
24+
- all dependencies at latest (MCP SDK 1.30, zod 4, uuid 14, ws 8.21.3, esbuild 0.28, vsce 3.9); npm audit 45 → 11
25+
- Julia pinned at 1.12.6 (juliaup default, CI, docs)
26+
127
## [0.2.7](https://github.com/JuliaPluto/advanced-vscode-extension/compare/v0.2.6...v0.2.7) (2025-11-16)
228

329
### Bug Fixes

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Press `F5` in VSCode to launch Extension Development Host window for testing.
7676

7777
- HTTP-based MCP server for AI assistant integration
7878
- Notebook management and code execution tools (see `packages/advanced-pluto-mcp/README.md` for the full list)
79-
- Server-Sent Events (SSE) for streaming responses
79+
- Streamable HTTP transport (modern MCP), with legacy SSE fallback on the same endpoint
8080
- Health check endpoint for monitoring
8181
- Shared PlutoManager instance with extension
8282
- Also bundled into the `@plutojl/cli` npm package (`src/cli/`), driven from the terminal via `npx @plutojl/cli run|tools|call`

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Not licensed yet (bear with us!)

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ so please don't raise issues about this in the official Pluto.jl channels or rep
1313
- **Notebook Interface**: Edit and run Pluto notebooks directly in VS Code
1414
- **Integrated Pluto Server**: Automatically manages Pluto server lifecycle
1515
- **Interactive Terminal**: Execute Julia code in an integrated terminal with rich output rendering
16-
- **MCP Server**: HTTP-based MCP server for AI assistants like Claude Desktop and GitHub Copilot
16+
- **MCP Server**: HTTP-based MCP server for AI assistants like Claude Code and GitHub Copilot
1717
- **Shared State**: Extension and MCP clients share the same Pluto server connection
1818
- **Real-time Execution**: Execute Julia code and see results immediately
1919
- **Cell Management**: Create, edit, and execute notebook cells
@@ -24,7 +24,7 @@ so please don't raise issues about this in the official Pluto.jl channels or rep
2424

2525
- **juliaup**: The Julia version manager must be installed and available in your PATH
2626
- Install from: https://github.com/JuliaLang/juliaup#installation
27-
- The extension will automatically install Julia 1.11.7 and Pluto.jl when needed
27+
- The extension will automatically install Julia 1.12.6 and Pluto.jl when needed
2828

2929
## Quick Start
3030

@@ -39,7 +39,7 @@ This extension contributes the following settings:
3939
- `pluto-notebook.port`: Port number for the Pluto server (default: 1234)
4040
- `pluto-notebook.mcpPort`: Port number for the MCP HTTP server (default: 3100)
4141
- `pluto-notebook.autoStartMcpServer`: Automatically start the MCP HTTP server when the extension activates (default: true)
42-
- `pluto-notebook.juliaVersion`: Julia version to use with juliaup (default: "1.11.7"). The extension will automatically install this version via juliaup if not already available. You can use specific versions like "1.10.0" or special channels like "release", "lts", etc.
42+
- `pluto-notebook.juliaVersion`: Julia version to use with juliaup (default: "1.12.6"). The extension will automatically install this version via juliaup if not already available. You can use specific versions like "1.10.0" or special channels like "release", "lts", etc.
4343

4444
## Available Commands
4545

@@ -69,9 +69,9 @@ This extension contributes the following settings:
6969

7070
The extension includes an MCP server that allows AI assistants to interact with your Pluto notebooks.
7171

72-
### Claude Desktop
72+
### Claude Code
7373

74-
Run the command `Pluto: Create MCP Config (Claude or Copilot)`, select "Claude Desktop", and restart Claude Desktop.
74+
Run the command `Pluto: Create MCP Config (Claude or Copilot)`, select "Claude Code", and the workspace `.mcp.json` is written for you.
7575

7676
### GitHub Copilot
7777

@@ -110,7 +110,9 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
110110

111111
## Dev TODO
112112

113-
- **VSCode notebook view does not reflect structural changes from MCP tools**: When cells are moved, deleted, or added via the MCP server (or any external client), the Pluto Worker's internal state updates automatically, but the VSCode notebook UI does not yet sync these changes. The `_handleCellReorder` handler in the controller is stubbed out. This only affects the VSCode view — external clients (the `@plutojl/cli` tool server) and Pluto's own UI stay fully in sync.
113+
- **Structural sync from external clients is new**: cells added, deleted, or reordered via the MCP tools (or Pluto's browser UI) now sync into the VSCode notebook view (`_handleCellReorder`). If you see cells duplicate or vanish when mixing VSCode edits with external edits, please file an issue with the Pluto Controller output-channel log.
114+
- **PlutoUI bonds are half-wired**: moving a slider in the VSCode renderer reaches Pluto (`set_bond` → controller → `worker.setBond`) and recomputation streams back, but bond _state_ distribution is stubbed (`get_notebook` returns an empty notebook in `renderer/renderer.tsx`), so widgets don't restore positions and JS-interop widgets (`request_js_link_response`) don't work. Needs controller→renderer state plumbing plus manual UI verification.
115+
- Upstream `@plutojl/rainbow` issues we work around: see `docs/UPSTREAM_RAINBOW_NOTES.md`.
114116

115117
## Support
116118

a.pluto.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
### A Pluto.jl notebook ###
2+
# v0.20.19
3+
4+
using Markdown
5+
using InteractiveUtils
6+
7+
# ╔═╡ d3289ef4-a7b8-11f0-2cb7-e1f1fe736ff6
8+
# ╠═╡ disabled = false
9+
# ╠═╡ show_logs = true
10+
# ╠═╡ skip_as_script = false
11+
using PlutoUI
12+
13+
# ╔═╡ be82cf10-a5ba-43e9-9ff5-c75a3dbeca7a
14+
# ╠═╡ show_logs = false
15+
# ╠═╡ disabled = false
16+
# ╠═╡ show_logs = false
17+
# ╠═╡ skip_as_script = false
18+
@bind x Slider(0:0.1:10)
19+
20+
# ╔═╡ 73225838-d89e-4008-929c-27fa92dd99ec
21+
# ╠═╡ show_logs = false
22+
# ╠═╡ disabled = false
23+
# ╠═╡ show_logs = false
24+
# ╠═╡ skip_as_script = false
25+
1+2
26+
27+
# ╔═╡ e0b2289c-8492-4674-90fd-f3178de4de19
28+
# ╠═╡ show_logs = false
29+
# ╠═╡ disabled = false
30+
# ╠═╡ show_logs = false
31+
# ╠═╡ skip_as_script = false
32+
x
33+
34+
# ╔═╡ 9470219e-00fa-4312-9cec-f31ac04bb26f
35+
# ╠═╡ show_logs = false
36+
# ╠═╡ disabled = false
37+
# ╠═╡ show_logs = false
38+
# ╠═╡ skip_as_script = false
39+
1+6
40+
41+
# ╔═╡ b8952e6e-346b-4cb3-babb-2106f8e39b2d
42+
# ╠═╡ show_logs = false
43+
# ╠═╡ disabled = false
44+
# ╠═╡ show_logs = false
45+
# ╠═╡ skip_as_script = false
46+
47+
48+
# ╔═╡ Cell order:
49+
# ╠═d3289ef4-a7b8-11f0-2cb7-e1f1fe736ff6
50+
# ╠═be82cf10-a5ba-43e9-9ff5-c75a3dbeca7a
51+
# ╠═73225838-d89e-4008-929c-27fa92dd99ec
52+
# ╠═e0b2289c-8492-4674-90fd-f3178de4de19
53+
# ╠═9470219e-00fa-4312-9cec-f31ac04bb26f
54+
# ╠═b8952e6e-346b-4cb3-babb-2106f8e39b2d

0 commit comments

Comments
 (0)