@mineo71/showtime 0.0.0-stage → 0.2.0

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +365 -2
  4. package/bin/showtime.mjs +5 -0
  5. package/docs/script.schema.json +1346 -0
  6. package/package.json +71 -4
  7. package/skill/SKILL.md +112 -0
  8. package/src/cli/format.ts +19 -0
  9. package/src/cli/main.ts +115 -0
  10. package/src/ffmpeg.ts +72 -0
  11. package/src/paths.ts +30 -0
  12. package/src/pipeline.ts +38 -0
  13. package/src/render/remotion/BrowserBar.tsx +61 -0
  14. package/src/render/remotion/Cursor.tsx +40 -0
  15. package/src/render/remotion/Intro.tsx +29 -0
  16. package/src/render/remotion/Overlays.tsx +95 -0
  17. package/src/render/remotion/Root.tsx +21 -0
  18. package/src/render/remotion/ShowtimeVideo.tsx +92 -0
  19. package/src/render/remotion/index.ts +4 -0
  20. package/src/render/render.ts +46 -0
  21. package/src/runner/browser.ts +122 -0
  22. package/src/runner/capture.ts +53 -0
  23. package/src/runner/check.ts +73 -0
  24. package/src/runner/encode.ts +49 -0
  25. package/src/runner/events.ts +15 -0
  26. package/src/runner/record.ts +96 -0
  27. package/src/runner/steps.ts +217 -0
  28. package/src/runner/time.ts +17 -0
  29. package/src/script/hash.ts +29 -0
  30. package/src/script/load.ts +36 -0
  31. package/src/script/schema.ts +144 -0
  32. package/src/setup.ts +41 -0
  33. package/src/take.ts +24 -0
  34. package/src/timeline/build.ts +103 -0
  35. package/src/timeline/camera.ts +69 -0
  36. package/src/timeline/cursor.ts +59 -0
  37. package/src/timeline/frames.ts +60 -0
  38. package/src/timeline/layout.ts +29 -0
  39. package/src/timeline/math.ts +7 -0
  40. package/src/timeline/timemap.ts +52 -0
  41. package/src/timeline/types.ts +111 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-10-13
4
+
5
+ - Published to npm as `@mineo71/showtime`.
6
+ - `showtime setup`: downloads the Chromium build Playwright drives and Remotion's headless
7
+ shell, and links the skill into Claude Code (`~/.claude/skills`) and Codex (`~/.agents/skills`).
8
+ - The skill works with Codex: agent-neutral instructions, a setup step.
9
+ - Browser window chrome around the take (`style.chrome`, `style.address`); the window eases in
10
+ after the intro card.
11
+ - `examples/fennel`: a showcase app and script; `npm run example`, `npm run media`.
12
+ - `docs/script.schema.json` for editor autocomplete (`"$schema"` in your script).
13
+
14
+ ## 0.1.0 — 2026-10-13
15
+
16
+ First release: `showtime check | record | render | frames | make`, the Claude Code skill,
17
+ auto-zoom, synthetic cursor, click ripples, spotlights, captions, auto speed-up of idle time,
18
+ intro card, privacy masks, stale-take detection.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oleh Rylskyj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,366 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <img src="docs/media/logo.svg" width="96" alt="showTime logo" />
4
+
5
+ # showTime
6
+
7
+ **Product demos, directed by AI.**
8
+
9
+ Your AI agent (Claude Code or Codex) writes a short JSON script. showTime clicks through your web app, films it and edits it
10
+ the way a motion designer would (auto-zoom, a smooth cursor, click ripples, spotlights, captions,
11
+ sped-up loading) and hands you a polished 1080p MP4.<br />
12
+ No timeline editor. No re-take when a caption changes.
13
+
14
+ [![tests](https://img.shields.io/github/actions/workflow/status/mineo71/showTime/ci.yml?branch=main&style=flat-square&label=tests)](https://github.com/mineo71/showTime/actions/workflows/ci.yml)
15
+ [![npm](https://img.shields.io/npm/v/@mineo71/showtime?style=flat-square&color=6366f1)](https://www.npmjs.com/package/@mineo71/showtime)
16
+ [![license](https://img.shields.io/badge/license-MIT-c026d3?style=flat-square)](LICENSE)
17
+ ![node](https://img.shields.io/badge/node-%E2%89%A5%2020-6366f1?style=flat-square)
18
+ ![built with](https://img.shields.io/badge/Playwright%20%2B%20Remotion-0f172a?style=flat-square)
19
+ ![Claude Code](https://img.shields.io/badge/Claude%20Code-skill-d97757?style=flat-square)
20
+ ![Codex](https://img.shields.io/badge/Codex-skill-10a37f?style=flat-square)
21
+
22
+ **[Install](#install)** · [Try it](#try-it) · [Use with Claude Code or Codex](#use-it-with-claude-code-or-codex) · [Script reference](#script-reference) · [CLI](#cli)
23
+
24
+ </div>
25
+
26
+ <div align="center">
27
+ <a href="docs/media/demo.mp4"><img src="docs/media/hero.webp" width="860" alt="A demo of a fictional invoicing app, filmed and edited by showTime: the camera zooms on a chart, a cursor fills in a new invoice, a spotlight lands on the total, and the sent invoice appears in the table" /></a>
28
+ <br />
29
+ <sub>Made by <a href="examples/fennel/demo.json">one 44-line script</a> and one command, with no manual editing. <a href="docs/media/demo.mp4">▶ Full-quality MP4</a></sub>
30
+ </div>
31
+
32
+ ---
33
+
34
+ ## Install
35
+
36
+ Needs **Node ≥ 20** and **ffmpeg** (`brew install ffmpeg` / `sudo apt install ffmpeg`).
37
+
38
+ ```bash
39
+ npm install -g @mineo71/showtime
40
+ showtime setup # downloads Chromium + the renderer, links the skill into Claude Code and Codex
41
+ showtime --help # check that it works
42
+ ```
43
+
44
+ Then ask your agent for a demo, or [film the bundled example](#try-it).
45
+
46
+ **Or let your agent install it.** Paste this into Claude Code or Codex:
47
+
48
+ > Install showTime: run `npm install -g @mineo71/showtime`, then `showtime setup`, and check
49
+ > that `showtime --help` works.
50
+
51
+ <sub>**Update:** `npm install -g @mineo71/showtime@latest`. **Switched Node versions with nvm?** Install it again under the new version. **From source:** clone the repo, then `npm install && npm link && showtime setup` (see [CONTRIBUTING](CONTRIBUTING.md)).</sub>
52
+
53
+ ## Why
54
+
55
+ Every pitch, launch post and README needs a product demo. Recording one is the easy part. The
56
+ editing eats the afternoon: zooming in on the button that matters, smoothing a jittery mouse,
57
+ cutting the spinner, adding captions, and doing it all again when the UI changes next week.
58
+
59
+ An AI can already write the shot list. **showTime gives it hands and an editing suite.** Claude
60
+ explores your app, writes the script, rehearses it, records it and reviews the stills. You watch
61
+ the result and ask for changes in plain words.
62
+
63
+ ## From script to screen
64
+
65
+ A step in the script says *what happens* and *what the viewer should notice*:
66
+
67
+ ```json
68
+ { "do": "hover", "target": ".bar[data-m='Oct']", "show": { "caption": "Your studio at a glance" } }
69
+ ```
70
+
71
+ showTime records the app untouched, then builds everything else from what happened during the take:
72
+
73
+ <table>
74
+ <tr>
75
+ <th width="50%">What the browser recorded</th>
76
+ <th width="50%">What showTime rendered</th>
77
+ </tr>
78
+ <tr>
79
+ <td><img src="docs/media/before.jpg" alt="Raw recording: the full dashboard, no cursor, no framing" /></td>
80
+ <td><img src="docs/media/after.jpg" alt="Rendered: browser window on a gradient, zoomed on the chart, cursor on the bar, caption below" /></td>
81
+ </tr>
82
+ </table>
83
+
84
+ The window, the camera move, the cursor and the caption are all added **after** the recording.
85
+ Change a caption, the colours or the pacing and re-render in seconds, without touching the app
86
+ again.
87
+
88
+ ## Features
89
+
90
+ <table>
91
+ <tr>
92
+ <td width="50%" valign="top">
93
+ <img src="docs/media/zoom.jpg" alt="Auto-zoom on a chart bar" /><br />
94
+ <b>🔍 Auto-zoom that follows the action</b><br />
95
+ <sub>The camera eases in on every click and keystroke. Zoom depth comes from the target's size, and nearby moments are joined by a pan instead of a bounce.</sub>
96
+ </td>
97
+ <td width="50%" valign="top">
98
+ <img src="docs/media/typing.jpg" alt="Typing into an autocomplete field" /><br />
99
+ <b>⌨️ Human typing, smooth cursor</b><br />
100
+ <sub>A synthetic cursor glides along eased paths, turns into an I-beam over text fields and types at a natural (seeded, repeatable) pace.</sub>
101
+ </td>
102
+ </tr>
103
+ <tr>
104
+ <td valign="top">
105
+ <img src="docs/media/spotlight.jpg" alt="Spotlight on the invoice total" /><br />
106
+ <b>🔦 Spotlights and captions</b><br />
107
+ <sub>Dim everything except the number that matters. One-line captions carry the story and fade in sync with the action.</sub>
108
+ </td>
109
+ <td valign="top">
110
+ <img src="docs/media/speed.jpg" alt="Clicking Send with a click ripple" /><br />
111
+ <b>⏩ Loading, compressed</b><br />
112
+ <sub>Spinners and network waits are detected and sped up 2–8×, so the demo never stalls. Click ripples show every press.</sub>
113
+ </td>
114
+ </tr>
115
+ <tr>
116
+ <td valign="top">
117
+ <img src="docs/media/tracked.jpg" alt="Invoice table with blurred billing emails and a spotlit new row" /><br />
118
+ <b>🙈 Privacy masks</b><br />
119
+ <sub>Blur emails, names or card numbers with plain CSS selectors. The blur is applied inside the browser, so no frame ever holds the real data.</sub>
120
+ </td>
121
+ <td valign="top">
122
+ <img src="docs/media/intro.jpg" alt="Intro card with title and subtitle" /><br />
123
+ <b>🎬 Intro card and window chrome</b><br />
124
+ <sub>Title card, a browser frame with your URL, a gradient backdrop, rounded corners and a shadow. Each of them is a <code>style</code> field.</sub>
125
+ </td>
126
+ </tr>
127
+ </table>
128
+
129
+ And under the hood:
130
+
131
+ - ✅ **Rehearsal mode.** `showtime check` runs the script at full speed and screenshots every step.
132
+ When a step fails it prints an ARIA snapshot of the page, so Claude can fix its own selectors.
133
+ - 🔁 **Stale-take detection.** The take stores a hash of the actions. Edit a caption and `render`
134
+ reuses the take. Edit a click and `render` tells you to `record` again.
135
+ - 🧪 **Self-review.** `showtime frames` renders a still per step so the agent can *look* at its
136
+ work before you do.
137
+ - 📐 **Typed scripts.** Point `"$schema"` at [`docs/script.schema.json`](docs/script.schema.json)
138
+ for autocomplete and validation in your editor.
139
+ - 🖥️ **Crisp at 1080p.** The app is captured at 2× device scale, so zoomed shots stay sharp.
140
+
141
+ ## Try it
142
+
143
+ After [installing](#install), film the example app from the video above. The example lives in
144
+ the repo:
145
+
146
+ ```bash
147
+ git clone https://github.com/mineo71/showTime.git && cd showTime
148
+ npm run example # terminal 1: serves examples/ on http://127.0.0.1:4800
149
+ showtime make examples/fennel/demo.json # terminal 2: → examples/fennel/demo.mp4
150
+ ```
151
+
152
+ `make` rehearses the script, records it in real time (about 25 s for this one) and renders it.
153
+ Expect a minute or two end to end.
154
+
155
+ ## Use it with Claude Code or Codex
156
+
157
+ `showtime setup` links the skill into `~/.claude/skills` for Claude Code and `~/.agents/skills`
158
+ for Codex (`--agent claude` or `--agent codex` picks one). Then ask for a demo the way you would
159
+ ask a person:
160
+
161
+ > Make a 30-second demo of the checkout flow on localhost:3000 for our investor deck.
162
+ > Blur customer emails.
163
+
164
+ The agent takes it from there:
165
+
166
+ 1. **Explores** the app (with the Playwright MCP if it has one) and picks the 2–4 moments that
167
+ sell it.
168
+ 2. **Writes** `demo/checkout.json`.
169
+ 3. **Rehearses** with `showtime check` until every step passes, fixing selectors from the ARIA
170
+ snapshot.
171
+ 4. **Records and renders** with `showtime record` and `showtime render`.
172
+ 5. **Reviews** the stills from `showtime frames`, tightens captions and pacing, and re-renders.
173
+
174
+ The skill ([`skill/SKILL.md`](skill/SKILL.md)) is a plain-Markdown
175
+ [Agent Skill](https://agentskills.io). Any agent that can run shell commands can follow it, and so
176
+ can a human with a text editor.
177
+
178
+ ## How it works
179
+
180
+ ```mermaid
181
+ flowchart LR
182
+ S["demo.json<br/>script"] --> C["check<br/>fast rehearsal"]
183
+ C -- green --> R["record<br/>Playwright + CDP"]
184
+ R --> V["raw.mp4<br/>clean video, no cursor"]
185
+ R --> E["events.json<br/>targets · clicks · idle"]
186
+ S --> T["timeline.json<br/>camera · cursor · captions · speed"]
187
+ E --> T
188
+ T --> M["Remotion"]
189
+ V --> M
190
+ M --> O["demo.mp4"]
191
+ ```
192
+
193
+ The core idea is to **separate capture from direction**. `record` drives a real Chromium through
194
+ the script and writes two things: a clean video with no cursor, and an event log of where every
195
+ target was, when each click landed and when the page sat idle. A pure function,
196
+ `buildTimeline(script, events)`, turns that log into speed segments, camera keyframes, cursor
197
+ paths, captions and spotlights. Remotion renders the result frame by frame.
198
+
199
+ So presentation is cheap to change and recording stays honest: the app really is clicked, typed
200
+ into and waited on, and everything cinematic is computed afterwards.
201
+
202
+ ## Script reference
203
+
204
+ ```json
205
+ {
206
+ "$schema": "https://raw.githubusercontent.com/mineo71/showTime/main/docs/script.schema.json",
207
+ "version": 1,
208
+ "meta": { "title": "Acme", "subtitle": "Reports in two clicks" },
209
+ "app": { "url": "http://localhost:3000/", "mask": [".email"] },
210
+ "setup": [{ "do": "click", "target": "text=Accept cookies" }],
211
+ "style": { "accent": "#6366f1" },
212
+ "scenes": [
213
+ {
214
+ "id": "report",
215
+ "steps": [
216
+ { "do": "click", "target": "role=button[name='New report']", "show": { "caption": "A report in one click" } },
217
+ { "do": "type", "target": "#name", "text": "Q3 revenue" },
218
+ { "do": "click", "target": "text=Create" },
219
+ { "do": "wait", "for": "text=Report ready" },
220
+ { "do": "hover", "target": ".total", "show": { "spotlight": true, "hold": 1500 } }
221
+ ]
222
+ }
223
+ ]
224
+ }
225
+ ```
226
+
227
+ `meta.title` adds an intro card. `setup` steps run before the camera starts (cookie banners,
228
+ onboarding). Scenes group steps for readability and for naming the stills.
229
+
230
+ <details>
231
+ <summary><b>Actions</b> (<code>do</code>)</summary>
232
+
233
+ | action | fields | notes |
234
+ |----------|---------------------------------|-------|
235
+ | `goto` | `url` | navigate and wait for `load` |
236
+ | `click` | `target` | cursor travels there, ripple on press |
237
+ | `type` | `target, text, pace?, clear?` | `pace`: `human` (default), `fast` or ms per character |
238
+ | `press` | `key` | `Enter`, `Escape`, `Meta+K`, … |
239
+ | `hover` | `target` | |
240
+ | `select` | `target, value` | picks a `<select>` option by value |
241
+ | `scroll` | `target` *or* `by` | smooth-scroll an element into view, or by pixels |
242
+ | `wait` | `for` *or* `ms` | prefer `for` (a selector): the wait is sped up automatically |
243
+ | `pause` | `ms` | a deliberate beat, always at 1× |
244
+
245
+ Targets are Playwright selectors: `role=button[name='Save']`, `text=Save`, `#id`,
246
+ `[data-testid=x]`, CSS, and `>>` chains.
247
+
248
+ </details>
249
+
250
+ <details>
251
+ <summary><b>Per-step presentation</b> (<code>show</code>)</summary>
252
+
253
+ | field | value | effect |
254
+ |-------------|----------------|--------|
255
+ | `caption` | string | caption while the step plays |
256
+ | `zoom` | 1–4 or `false` | override the auto-zoom depth, or skip zooming |
257
+ | `spotlight` | boolean | dim everything except the target |
258
+ | `hold` | ms | extra real time after the step (changes the take) |
259
+ | `speed` | 0.25–16 | playback speed for this step |
260
+
261
+ </details>
262
+
263
+ <details>
264
+ <summary><b>Look</b> (<code>style</code>)</summary>
265
+
266
+ | field | default | |
267
+ |--------------|--------------------|-|
268
+ | `background` | indigo gradient | any CSS background |
269
+ | `chrome` | `browser` | browser window frame, or `none` |
270
+ | `address` | the app URL | address-bar text |
271
+ | `accent` | `#6366f1` | ripples, spotlight ring, intro rule |
272
+ | `padding` | `0.06` | backdrop around the window, as a fraction of the frame |
273
+ | `radius` | `14` | window corner radius, px |
274
+ | `shadow` | `true` | window drop shadow |
275
+ | `font` | Inter / system UI | captions and intro card |
276
+ | `autoZoom` | `true` | zoom on clicks, typing, hovers and selects |
277
+ | `autoSpeed` | `true` | speed up idle stretches longer than 0.8 s |
278
+ | `maxZoom` | `1.8` | cap for auto-zoom |
279
+
280
+ </details>
281
+
282
+ <details>
283
+ <summary><b>App</b> (<code>app</code>)</summary>
284
+
285
+ | field | default | |
286
+ |---------------------|------------|-|
287
+ | `url` | (required) | where the take starts |
288
+ | `viewport` | 1440 × 900 | CSS pixels |
289
+ | `deviceScaleFactor` | `2` | capture resolution multiplier |
290
+ | `storageState` | none | Playwright storage-state file (relative to the script) to start logged in |
291
+ | `colorScheme` | none | `light` or `dark`; also picks the browser-bar theme |
292
+ | `locale`, `timezoneId` | none | passed to the browser context |
293
+ | `mask` | `[]` | CSS selectors to blur (plain CSS, not `text=`) |
294
+
295
+ </details>
296
+
297
+ ## CLI
298
+
299
+ | command | what it does |
300
+ |---|---|
301
+ | `showtime setup [--agent claude\|codex\|all] [--no-browsers]` | download the browsers, link the skill |
302
+ | `showtime make <script> [-o out.mp4]` | check → record → render |
303
+ | `showtime check <script> [--json] [--headed]` | rehearse; a screenshot per step; the first failure with an ARIA snapshot |
304
+ | `showtime record <script> [--headed] [--keep-frames]` | record a take into `.showtime/<name>/take` |
305
+ | `showtime render <script> [-o out.mp4]` | render from the last take, without re-recording |
306
+ | `showtime frames <script>` | QA stills: the intro plus one per step |
307
+ | `showtime schema` | print the script's JSON Schema |
308
+
309
+ The video lands next to the script (`demo/pitch.json` → `demo/pitch.mp4`). Working files live in
310
+ `.showtime/<name>/`, which is worth adding to your `.gitignore`.
311
+
312
+ ## Tips for a great demo
313
+
314
+ - **One idea per caption, six words at most.** Caption the benefit ("Paid in two clicks"), not
315
+ the click ("Click Pay").
316
+ - **Let payoffs breathe.** After the screen that matters, add `hold` (800–2000 ms) and a
317
+ `spotlight` on the one number that sells it.
318
+ - **Never `pause` for loading.** Use `wait` with a selector and auto-speed compresses the spinner.
319
+ - **Start clean.** Use `setup` for cookie banners, `app.storageState` to skip the login, `app.mask`
320
+ for anything personal, and seed data you can reset between `check` and `record`.
321
+
322
+ ## FAQ
323
+
324
+ **Which apps does it work with?** Any web app Chromium can open: a localhost dev server, staging
325
+ or production. Logged-in flows work through `app.storageState`.
326
+
327
+ **Does showTime call an AI model?** No. showTime is deterministic and needs no API keys. The AI
328
+ part (writing, fixing and reviewing the script) happens in your agent, guided by the skill.
329
+
330
+ **Why not just screen-record?** You can, once. A screen recording can't be re-edited when the copy
331
+ changes, the cursor is wherever your hand was, and the spinner plays at full length. With
332
+ showTime, edits are a re-render and a UI change is one command.
333
+
334
+ **Does it run on Linux or Windows?** It is developed on macOS. Linux should work since it only
335
+ needs Playwright and ffmpeg. Windows is untested.
336
+
337
+ ## Roadmap
338
+
339
+ - 🎙️ Narration: text-to-speech per scene, with steps stretched to fit the audio, and burnt-in subtitles.
340
+ - 📱 Vertical 9:16 and square 1:1 cuts with a follow-cam, from the same take.
341
+ - 🎞️ Deterministic frame-by-frame capture in virtual time, for a perfect 60 fps.
342
+ - 🧩 An MCP server for Claude Desktop, and a desktop studio for timeline tweaks.
343
+ - 📲 Mobile (iOS Simulator) and desktop-app drivers that produce the same `events.json`.
344
+
345
+ ## Development
346
+
347
+ ```bash
348
+ npm test # unit tests (seconds)
349
+ npm run test:int # Playwright + ffmpeg + Remotion, end to end (minutes)
350
+ npm run typecheck
351
+ npm run media # rebuild the README media from examples/fennel (see CONTRIBUTING)
352
+ ```
353
+
354
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the architecture and how to work on it. The design
355
+ document is in [`docs/superpowers/specs`](docs/superpowers/specs/2026-10-13-showtime-design.md).
356
+
357
+ ## License
358
+
359
+ [MIT](LICENSE) © 2026 Oleh Rylskyj.
360
+
361
+ showTime renders with [Remotion](https://www.remotion.dev), which has its own
362
+ [license](https://www.remotion.dev/license): it is free for individuals, non-profits and companies
363
+ of up to 3 people, and larger companies need a Remotion company license. The renderer only reads
364
+ `timeline.json`, so it can be swapped out.
365
+
366
+ <sub>Fennel, the app in the example, is fictional. It was built for this README and its data is made up.</sub>
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { register } from 'tsx/esm/api';
3
+
4
+ register();
5
+ await import('../src/cli/main.ts');