@lopecode/channel 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # @lopecode/channel
2
+
3
+ Pair program with Claude Code inside [Lopecode](https://tomlarkworthy.github.io/lopecode/) notebooks. An MCP server that bridges browser-based Observable notebooks and Claude Code via WebSocket, enabling real-time collaboration: chat, define cells, watch reactive variables, run tests, and manipulate the DOM — all from inside the notebook.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Requires Bun (https://bun.sh)
9
+ bun install -g @lopecode/channel
10
+ claude mcp add lopecode bunx @lopecode/channel
11
+
12
+ # Start Claude Code with channels enabled
13
+ claude --dangerously-load-development-channels server:lopecode
14
+ ```
15
+
16
+ Then ask Claude: **"Open a lopecode notebook"**
17
+
18
+ Claude gets a pairing token, opens the notebook in your browser, and auto-connects. No manual setup needed.
19
+
20
+ ## What is Lopecode?
21
+
22
+ Lopecode notebooks are self-contained HTML files built on the [Observable runtime](https://github.com/observablehq/runtime). Each notebook contains:
23
+
24
+ - **Modules** — collections of reactive cells (code units)
25
+ - **Embedded dependencies** — everything needed to run, in a single file
26
+ - **A multi-panel UI** (lopepage) — view and edit multiple modules side by side
27
+
28
+ The Observable runtime provides **reactive dataflow**: cells automatically recompute when their dependencies change, similar to a spreadsheet.
29
+
30
+ ## How Pairing Works
31
+
32
+ ```
33
+ Browser (Notebook) ←→ WebSocket ←→ Channel Server (Bun) ←→ MCP stdio ←→ Claude Code
34
+ ```
35
+
36
+ 1. The channel server starts a local WebSocket server and generates a pairing token (`LOPE-PORT-XXXX`)
37
+ 2. Claude opens a notebook URL with `&cc=TOKEN` in the hash
38
+ 3. The notebook auto-connects to the WebSocket server
39
+ 4. Claude can now use MCP tools to interact with the live notebook
40
+
41
+ ## Observable Cell Syntax
42
+
43
+ Lopecode cells use [Observable JavaScript](https://observablehq.com/@observablehq/observable-javascript) syntax. Here's what you need to know:
44
+
45
+ ### Named Cells
46
+
47
+ ```javascript
48
+ // A cell is a named expression. It re-runs when dependencies change.
49
+ x = 42
50
+ greeting = `Hello, ${name}!` // depends on the 'name' cell
51
+ ```
52
+
53
+ ### Markdown
54
+
55
+ ```javascript
56
+ // Use the md tagged template literal for rich text
57
+ md`# My Title
58
+
59
+ Some **bold** text and a list:
60
+ - Item 1
61
+ - Item 2
62
+ `
63
+ ```
64
+
65
+ ### HTML
66
+
67
+ ```javascript
68
+ // Use htl.html for DOM elements
69
+ htl.html`<div style="color: red">Hello</div>`
70
+ ```
71
+
72
+ ### Imports
73
+
74
+ ```javascript
75
+ // Import from other modules in the notebook
76
+ import {md} from "@tomlarkworthy/editable-md"
77
+ import {chart} from "@tomlarkworthy/my-visualization"
78
+ ```
79
+
80
+ ### viewof — Interactive Inputs
81
+
82
+ ```javascript
83
+ // viewof creates two cells:
84
+ // "viewof slider" — the DOM element (a range input)
85
+ // "slider" — the current value (a number)
86
+ viewof slider = Inputs.range([0, 100], {label: "Value", value: 50})
87
+
88
+ // Other cells can depend on the value
89
+ doubled = slider * 2
90
+ ```
91
+
92
+ Common inputs: `Inputs.range`, `Inputs.select`, `Inputs.text`, `Inputs.toggle`, `Inputs.button`, `Inputs.table`.
93
+
94
+ ### mutable — Imperative State
95
+
96
+ ```javascript
97
+ // mutable allows imperative updates from other cells
98
+ mutable counter = 0
99
+
100
+ increment = {
101
+ mutable counter++;
102
+ return counter;
103
+ }
104
+ ```
105
+
106
+ ### Generators — Streaming Values
107
+
108
+ ```javascript
109
+ // Yield successive values over time
110
+ ticker = {
111
+ let i = 0;
112
+ while (true) {
113
+ yield i++;
114
+ await Promises.delay(1000);
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### Block Cells
120
+
121
+ ```javascript
122
+ // Use braces for multi-statement cells
123
+ result = {
124
+ const data = await fetch("https://api.example.com/data").then(r => r.json());
125
+ const filtered = data.filter(d => d.value > 10);
126
+ return filtered;
127
+ }
128
+ ```
129
+
130
+ ## Testing
131
+
132
+ Lopecode uses a reactive testing pattern. Any cell named `test_*` is a test:
133
+
134
+ ```javascript
135
+ test_addition = {
136
+ const result = add(2, 2);
137
+ if (result !== 4) throw new Error(`Expected 4, got ${result}`);
138
+ return "2 + 2 = 4"; // shown on success
139
+ }
140
+
141
+ test_greeting = {
142
+ if (typeof greeting !== "string") throw new Error("Expected string");
143
+ return `greeting is: ${greeting}`;
144
+ }
145
+ ```
146
+
147
+ Tests pass if they don't throw. Use `run_tests` to execute all `test_*` cells.
148
+
149
+ ## MCP Tools Reference
150
+
151
+ | Tool | Description |
152
+ |------|-------------|
153
+ | `get_pairing_token` | Get the session pairing token |
154
+ | `reply` | Send markdown to the notebook chat |
155
+ | `define_cell` | **Primary tool.** Define a cell using Observable source code |
156
+ | `list_cells` | List cells with names, inputs, and source |
157
+ | `get_variable` | Read a runtime variable's current value |
158
+ | `define_variable` | Low-level: define a variable with a function string |
159
+ | `delete_variable` | Remove a variable |
160
+ | `list_variables` | List all named variables |
161
+ | `create_module` | Create a new empty module |
162
+ | `delete_module` | Remove a module and all its variables |
163
+ | `watch_variable` | Subscribe to reactive updates |
164
+ | `unwatch_variable` | Unsubscribe from updates |
165
+ | `run_tests` | Run all `test_*` cells |
166
+ | `eval_code` | Run ephemeral JS in the browser (not persisted) |
167
+ | `export_notebook` | Save the notebook to disk (persists cells) |
168
+ | `fork_notebook` | Create a copy as a sibling HTML file |
169
+
170
+ ### Tool Usage Tips
171
+
172
+ - **`define_cell`** is the main tool for creating content. It accepts Observable source and compiles it via the toolchain.
173
+ - **`eval_code`** is for throwaway actions (DOM hacks, debugging). Effects are lost on reload.
174
+ - **`define_variable`** is a low-level escape hatch — prefer `define_cell`.
175
+ - Always specify `module` when targeting a specific module.
176
+ - Use `export_notebook` after defining cells to persist them across reloads.
177
+
178
+ ## Typical Workflow
179
+
180
+ ```
181
+ 1. create_module("@tomlarkworthy/my-app")
182
+ 2. define_cell('import {md} from "@tomlarkworthy/editable-md"', module: "...")
183
+ 3. define_cell('title = md`# My App`', module: "...")
184
+ 4. define_cell('viewof name = Inputs.text({label: "Name"})', module: "...")
185
+ 5. define_cell('greeting = md`Hello, **${name}**!`', module: "...")
186
+ 6. export_notebook() // persist to disk
187
+ ```
188
+
189
+ ## Starting from a Notebook
190
+
191
+ If you see the `@tomlarkworthy/claude-code-pairing` panel in a notebook but Claude isn't connected:
192
+
193
+ 1. Install Bun: https://bun.sh
194
+ 2. Install the plugin: `bun install -g @lopecode/channel`
195
+ 3. Register with Claude: `claude mcp add lopecode bunx @lopecode/channel`
196
+ 4. Start Claude: `claude --dangerously-load-development-channels server:lopecode`
197
+ 5. Ask Claude to connect — it will provide a URL with an auto-connect token
198
+
199
+ ## Environment Variables
200
+
201
+ - `LOPECODE_PORT` — WebSocket server port (default: random free port)
202
+
203
+ ## License
204
+
205
+ MIT