Procházet zdrojové kódy

chore: bump to v2.5.0 — remove /canvas command and canvas-tui package

The canvas command was experimental, Warp-terminal-specific, and unused.
Removing it eliminates the only npm runtime-dep surface in claude-mods,
leaving the repo as markdown + bash only.

Version: 2.4.12 -> 2.5.0
Minor bump (not patch) because removing a documented public API (/canvas)
is a breaking change for anyone who'd integrated it.

Removed:
  - commands/canvas.md (340 lines)
  - canvas-tui/ entire directory (17 source files, 2096-line package-lock.json,
    117-line bundled README, 57-line package.json, tsconfig, etc.)

Manifest changes (.claude-plugin/plugin.json):
  - version: 2.4.12 -> 2.5.0
  - description: "3 commands" -> "2 commands"
  - keywords: drop "canvas", "terminal-ui"
  - components.commands: drop commands/canvas.md

Doc updates:
  - AGENTS.md: "3 commands ... and experimental (/canvas)" -> "2 commands"
  - README.md: removed canvas table row, updated commands-paragraph,
    added v2.5.0 Recent Updates entry covering the removal
  - docs/COMMAND-SKILL-PATTERN.md: tree showing 2 files, dropped
    "Experimental/WIP -> Command (canvas)" row
  - docs/RESERVED-COMMANDS.md: dropped canvas from current-claude-mods list
  - docs/SKILL-SUBAGENT-REFERENCE.md: dropped /canvas row from session
    commands analysis
  - docs/PLAN.md: Commands count 3->2, dropped "Experimental: /canvas"
    line and "/canvas - Experimental (Warp-specific)" footer

This integrates the canvas-teardown work originally done in sibling
branch claude/sad-almeida-20699c by an Opus 4.7 session, applied on
top of v2.4.12 (which already shipped portless-ops, process-compose-ops,
and the summon+fleet-ops manifest catch-up).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0xDarkMatter před 2 měsíci
rodič
revize
e43e60c08f

+ 4 - 7
.claude-plugin/plugin.json

@@ -1,7 +1,7 @@
 {
   "name": "claude-mods",
-  "version": "2.4.12",
-  "description": "Custom commands, skills, and agents for Claude Code - session continuity, 23 expert agents, 75 skills, 3 commands, 6 rules, 4 hooks, 13 output styles, modern CLI tools",
+  "version": "2.5.0",
+  "description": "Custom commands, skills, and agents for Claude Code - session continuity, 23 expert agents, 75 skills, 2 commands, 6 rules, 4 hooks, 13 output styles, modern CLI tools",
   "author": "0xDarkMatter",
   "repository": "https://github.com/0xDarkMatter/claude-mods",
   "license": "MIT",
@@ -12,15 +12,12 @@
     "skills",
     "session-management",
     "cli-tools",
-    "output-styles",
-    "canvas",
-    "terminal-ui"
+    "output-styles"
   ],
   "components": {
     "commands": [
       "commands/sync.md",
-      "commands/save.md",
-      "commands/canvas.md"
+      "commands/save.md"
     ],
     "agents": [
       "agents/astro-expert.md",

+ 1 - 1
AGENTS.md

@@ -4,7 +4,7 @@
 
 This is **claude-mods** - a collection of custom extensions for Claude Code:
 - **23 expert agents** for specialized domains (React, Python, Go, Rust, AWS, git, etc.)
-- **3 commands** for session management (/sync, /save) and experimental features (/canvas)
+- **2 commands** for session management (/sync, /save)
 - **75 skills** for CLI tools, patterns, workflows, and development tasks
 - **13 output styles** for response personality (Vesper, Spartan, Mentor, Executive, Pair, Atlas, Coach, Harbour, Meridian, Noir, Roast, Sage, Scout)
 - **4 hooks** for pre-commit linting, post-edit formatting, dangerous command warnings, and pmail notifications

+ 3 - 3
README.md

@@ -22,10 +22,11 @@ From Python async patterns to Rust ownership models, from AWS Fargate deployment
 
 ## Recent Updates
 
-**v2.4.12** (May 2026)
+**v2.5.0** (May 2026)
 - 🌐 **`portless-ops` skill** - Local-dev HTTPS proxy operations for Vercel Labs' [portless](https://github.com/vercel-labs/portless). Wraps the canonical upstream `SKILL.md` and `oauth/SKILL.md` (vendored verbatim into `references/` since the npm package only ships `dist/`) and overlays operational patterns we've validated: the static-alias pattern for pairing portless with external supervisors (Process Compose, PM2, Docker), TLD selection decision tree (`.test`/`.dev`/`.localhost`/custom-owned), Windows-specific gotchas (`openssl` PATH from Git for Windows, `certutil` quirks, curl-vs-browser cert handling, PS 5.1 vs 7+ flag differences), the clean-reset procedure when changing TLDs (because `portless alias --remove` appends the active TLD), and three runnable scripts: `install-portless.ps1` (audits the npm tarball for known supply-chain IOCs *before* installing), `reset-state.ps1` (full state wipe + re-register), `sync-aliases-from-yaml.ps1` (derives portless aliases from a supervisor's YAML). Four `portless.json` asset templates cover single-app, monorepo, custom-TLD-documented, and `package.json`-inline patterns.
 - 🎛️ **`process-compose-ops` skill** - Comprehensive operations for [Process Compose](https://github.com/F1bonacc1/process-compose), the Go-binary supervisor replacing PM2/supervisord/Foreman for non-containerised local services. Six reference files: `schema-reference.md` (full YAML schema with field semantics, defaults, and command-quoting gotchas including Windows-PATH backslash handling), `probe-patterns.md` (readiness probe recipes per stack — Python/Go/Node/TCP-only/daemons), `dependency-patterns.md` (`depends_on` patterns: companion daemons, DB-before-app, tunnel-after-service, one-shot init), `tui-shortcuts.md` (TUI keybindings cheatsheet, status legend, search/sort), `boot-persistence-windows.md` (Task Scheduler with `S4U` logon and PATH-aware wrapper script), `supply-chain-verification.md` (SHA-256 verification procedure for the binary). Four runnable scripts: `install-process-compose.ps1` (verified download + extract + writes `VERIFICATION.md`), `verify-binary.ps1` (re-verifies committed binary hash), plus boot wrapper and Task Scheduler installer templates. Five YAML assets: Python service, Django+companions, Go binary, Cloudflare tunnel pattern, cron job. Material derived from a 3-hour production migration from PM2+Caddy+Dagu to Process Compose+portless, anonymised for general use.
 - 📦 **Plugin manifest catch-up** - `summon` (v2.4.11) and `fleet-ops` (post-v2.4.11) were committed and listed in README but never added to `.claude-plugin/plugin.json`'s `components.skills` array, so they weren't being indexed by the plugin system. Both registered correctly now alongside the new pair.
+- 🗑️ **`/canvas` command and `canvas-tui` package removed** - The canvas command was experimental, Warp-terminal-specific, and unused. Deletion removes the only npm runtime-dep surface in claude-mods (2,096-line lockfile + 17 TypeScript/React source files + 117-line bundled README), leaving the repo as markdown + bash only. Minor bump rather than patch because it removes a documented public API (`/canvas`). Co-developed in branch `claude/sad-almeida-20699c` by a sibling Opus 4.7 session and integrated into this release.
 
 **v2.4.11** (May 2026)
 - ✨ **`summon` skill** - Push Claude Desktop Code-tab sessions across accounts so they appear in the next account you switch to. Best run *before* switching — while still on your current near-limit account, push mid-flight sessions to the destination, then Logout/Login as the natural switch. Default is copy (sessions visible from both accounts); `--move` for lean cleanup. Hierarchical Account → Project → Session picker with global numbering, `--peek <id>` for transcript preview, `--list-accounts` inventory, recency aliases (`--1d/--3d/--7d/--all`), 8-hint rotating tip system. Output follows `docs/DESIGN.md` (Terminal Panel Design System).
@@ -230,7 +231,6 @@ See [skill-creator](skills/skill-creator/) for the complete guide.
 |---------|-------------|
 | [sync](commands/sync.md) | Session bootstrap - restore tasks, plan, git/PR context. Suggests `--resume` and `--from-pr`. |
 | [save](commands/save.md) | Persist tasks, plan, git/PR context, and session summary to native memory. |
-| [canvas](commands/canvas.md) | Terminal canvas for content drafting with live markdown preview. Requires Warp terminal. (Experimental) |
 
 ### Skills
 
@@ -548,7 +548,7 @@ When using multiple MCP servers (Chrome DevTools, Vibe Kanban, etc.), their tool
 
 ### Skills Over Commands
 
-Most functionality lives in skills rather than commands. Skills get slash-hint discovery via trigger keywords and load on-demand, reducing context overhead. Only session management (`/sync`, `/save`) and experimental features (`/canvas`) remain as commands.
+Most functionality lives in skills rather than commands. Skills get slash-hint discovery via trigger keywords and load on-demand, reducing context overhead. Only session management (`/sync`, `/save`) remains as commands.
 
 See `docs/COMMAND-SKILL-PATTERN.md` for details.
 

+ 0 - 5
canvas-tui/.gitignore

@@ -1,5 +0,0 @@
-node_modules/
-dist/
-*.log
-.DS_Store
-.claude/

+ 0 - 117
canvas-tui/README.md

@@ -1,117 +0,0 @@
-# @claude-mods/canvas-tui
-
-> **Beta** - Under active development. Features may change.
-
-Terminal canvas for Claude Code - live markdown preview in split panes.
-
-Works with any terminal that supports split panes: **Warp**, **tmux**, **iTerm2**, **Windows Terminal**, and more.
-
-## Installation
-
-### From claude-mods repo (local)
-
-```bash
-cd claude-mods/canvas-tui
-npm link
-```
-
-Then use `canvas-tui` from anywhere on your machine.
-
-### From npm (coming soon)
-
-```bash
-npm install -g @claude-mods/canvas-tui
-```
-
-## Usage
-
-```bash
-# Watch default location (.claude/canvas/content.md)
-canvas-tui --watch
-
-# Watch specific directory
-canvas-tui --watch ./my-canvas
-
-# Watch specific file
-canvas-tui --file ./draft.md
-
-# Disable mouse scroll capture (keyboard only)
-canvas-tui --watch --no-mouse
-
-# Show help
-canvas-tui --help
-```
-
-## Terminal Setup
-
-### Warp
-```bash
-# Split pane: Ctrl+Shift+D (Windows/Linux) or Cmd+Shift+D (Mac)
-# Or right-click > Split Pane
-canvas-tui --watch
-```
-
-### tmux
-```bash
-# Split horizontally and run canvas
-tmux split-window -h 'canvas-tui --watch'
-
-# Or split vertically
-tmux split-window -v 'canvas-tui --watch'
-
-# From existing tmux session, split current pane
-# Ctrl+B then % (horizontal) or " (vertical)
-```
-
-### iTerm2
-```bash
-# Split pane: Cmd+D (vertical) or Cmd+Shift+D (horizontal)
-canvas-tui --watch
-```
-
-### Windows Terminal
-```bash
-# Split pane: Alt+Shift+D
-# Or use wt command:
-wt split-pane --horizontal canvas-tui --watch
-```
-
-## Controls
-
-| Key | Action |
-|-----|--------|
-| `Up/Down` | Scroll line by line |
-| `Page Up/Down` | Scroll by page |
-| `g` | Go to top |
-| `G` | Go to bottom |
-| `q` / `Ctrl+C` | Quit |
-| `r` | Refresh |
-| Mouse wheel | Scroll (terminal-dependent) |
-
-**Note:** Mouse wheel scrolling works in iTerm2, Terminal.app, and most xterm-compatible terminals. In Warp, use arrow keys or Warp's native scrollbar instead.
-
-## Integration with Claude Code
-
-This TUI works with the `/canvas` command in Claude Code:
-
-1. Run `/canvas start --type email` in Claude Code
-2. Open a split pane in your terminal
-3. Run `canvas-tui --watch` in the split pane
-4. Claude writes content, you see live preview
-
-## File Structure
-
-```
-.claude/canvas/
-├── content.md      # Shared content (Claude writes, TUI renders)
-└── meta.json       # Session metadata
-```
-
-## Requirements
-
-- Node.js >= 18.0.0
-- Terminal with ANSI color support
-
-## License
-
-MIT

+ 0 - 2
canvas-tui/bin/canvas.js

@@ -1,2 +0,0 @@
-#!/usr/bin/env node
-import('../dist/index.js');

+ 0 - 2096
canvas-tui/package-lock.json

@@ -1,2096 +0,0 @@
-{
-  "name": "@claude-mods/canvas-tui",
-  "version": "0.1.0",
-  "lockfileVersion": 3,
-  "requires": true,
-  "packages": {
-    "": {
-      "name": "@claude-mods/canvas-tui",
-      "version": "0.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "@inkjs/ui": "^2.0.0",
-        "chalk": "^5.3.0",
-        "chokidar": "^4.0.3",
-        "cli-markdown": "^3.5.1",
-        "ink": "^5.0.1",
-        "marked": "^15.0.4",
-        "marked-terminal": "^7.2.1",
-        "meow": "^13.2.0",
-        "react": "^18.3.1"
-      },
-      "bin": {
-        "canvas": "bin/canvas.js",
-        "canvas-tui": "bin/canvas.js"
-      },
-      "devDependencies": {
-        "@types/node": "^22.10.5",
-        "@types/react": "^18.3.18",
-        "typescript": "^5.7.2"
-      },
-      "engines": {
-        "node": ">=18.0.0"
-      }
-    },
-    "node_modules/@alcalzone/ansi-tokenize": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.1.3.tgz",
-      "integrity": "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^6.2.1",
-        "is-fullwidth-code-point": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=14.13.1"
-      }
-    },
-    "node_modules/@colors/colors": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
-      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
-      "license": "MIT",
-      "optional": true,
-      "engines": {
-        "node": ">=0.1.90"
-      }
-    },
-    "node_modules/@inkjs/ui": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@inkjs/ui/-/ui-2.0.0.tgz",
-      "integrity": "sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg==",
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^5.3.0",
-        "cli-spinners": "^3.0.0",
-        "deepmerge": "^4.3.1",
-        "figures": "^6.1.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "ink": ">=5"
-      }
-    },
-    "node_modules/@mdit/plugin-alert": {
-      "version": "0.22.3",
-      "resolved": "https://registry.npmjs.org/@mdit/plugin-alert/-/plugin-alert-0.22.3.tgz",
-      "integrity": "sha512-9g99rjLCFd8upA/DXbhGmEM7GMFocy6SRk4OekxuAy9t1aDOE/r5IJgUbBIvc9kMkg39ug0yXtMkKwAt2zp5Hg==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/markdown-it": "^14.1.2"
-      },
-      "peerDependencies": {
-        "markdown-it": "^14.1.0"
-      },
-      "peerDependenciesMeta": {
-        "markdown-it": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@sindresorhus/is": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
-      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/is?sponsor=1"
-      }
-    },
-    "node_modules/@types/linkify-it": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
-      "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
-      "license": "MIT"
-    },
-    "node_modules/@types/markdown-it": {
-      "version": "14.1.2",
-      "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
-      "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/linkify-it": "^5",
-        "@types/mdurl": "^2"
-      }
-    },
-    "node_modules/@types/marked": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-6.0.0.tgz",
-      "integrity": "sha512-jmjpa4BwUsmhxcfsgUit/7A9KbrC48Q0q8KvnY107ogcjGgTFDlIL3RpihNpx2Mu1hM4mdFQjoVc4O6JoGKHsA==",
-      "deprecated": "This is a stub types definition. marked provides its own type definitions, so you do not need this installed.",
-      "license": "MIT",
-      "dependencies": {
-        "marked": "*"
-      }
-    },
-    "node_modules/@types/mdurl": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
-      "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
-      "license": "MIT"
-    },
-    "node_modules/@types/node": {
-      "version": "22.19.3",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz",
-      "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "undici-types": "~6.21.0"
-      }
-    },
-    "node_modules/@types/prop-types": {
-      "version": "15.7.15",
-      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
-      "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/react": {
-      "version": "18.3.27",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
-      "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
-      "devOptional": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/prop-types": "*",
-        "csstype": "^3.2.2"
-      }
-    },
-    "node_modules/ansi-align": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
-      "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
-      "license": "ISC",
-      "dependencies": {
-        "string-width": "^4.1.0"
-      }
-    },
-    "node_modules/ansi-align/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/ansi-align/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "license": "MIT"
-    },
-    "node_modules/ansi-align/node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/ansi-align/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/ansi-align/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/ansi-escapes": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz",
-      "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==",
-      "license": "MIT",
-      "dependencies": {
-        "environment": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/ansi-regex": {
-      "version": "6.2.2",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
-      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
-      }
-    },
-    "node_modules/ansi-styles": {
-      "version": "6.2.3",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
-      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/any-promise": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
-      "license": "MIT"
-    },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "license": "Python-2.0"
-    },
-    "node_modules/arity-n": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz",
-      "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==",
-      "license": "MIT"
-    },
-    "node_modules/auto-bind": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz",
-      "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==",
-      "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/boxen": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
-      "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-align": "^3.0.1",
-        "camelcase": "^8.0.0",
-        "chalk": "^5.3.0",
-        "cli-boxes": "^3.0.0",
-        "string-width": "^7.2.0",
-        "type-fest": "^4.21.0",
-        "widest-line": "^5.0.0",
-        "wrap-ansi": "^9.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/buffer-from": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-      "license": "MIT"
-    },
-    "node_modules/camelcase": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
-      "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=16"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/chalk": {
-      "version": "5.6.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
-      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
-      "license": "MIT",
-      "engines": {
-        "node": "^12.17.0 || ^14.13 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/chalk-string": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/chalk-string/-/chalk-string-4.0.0.tgz",
-      "integrity": "sha512-/EAe8GJnW2MKxrjiIoAxySKETk+loIfk0Qy/ov614CMqipR5i5tw0NIgM+vy0TiGizq3wgd3lK0S4/oF9Zrpig==",
-      "license": "MIT",
-      "dependencies": {
-        "colors-option": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=18.18.0"
-      }
-    },
-    "node_modules/change-case": {
-      "version": "5.4.4",
-      "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz",
-      "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==",
-      "license": "MIT"
-    },
-    "node_modules/char-regex": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
-      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/chokidar": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
-      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
-      "license": "MIT",
-      "dependencies": {
-        "readdirp": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 14.16.0"
-      },
-      "funding": {
-        "url": "https://paulmillr.com/funding/"
-      }
-    },
-    "node_modules/chroma-js": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz",
-      "integrity": "sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ=="
-    },
-    "node_modules/cli-boxes": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
-      "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/cli-cursor": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
-      "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
-      "license": "MIT",
-      "dependencies": {
-        "restore-cursor": "^4.0.0"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/cli-highlight": {
-      "version": "2.1.11",
-      "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
-      "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
-      "license": "ISC",
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "highlight.js": "^10.7.1",
-        "mz": "^2.4.0",
-        "parse5": "^5.1.1",
-        "parse5-htmlparser2-tree-adapter": "^6.0.0",
-        "yargs": "^16.0.0"
-      },
-      "bin": {
-        "highlight": "bin/highlight"
-      },
-      "engines": {
-        "node": ">=8.0.0",
-        "npm": ">=5.0.0"
-      }
-    },
-    "node_modules/cli-highlight/node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/cli-highlight/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/cli-html": {
-      "version": "4.5.1",
-      "resolved": "https://registry.npmjs.org/cli-html/-/cli-html-4.5.1.tgz",
-      "integrity": "sha512-mSq2rI+pJVFCTxNEXo38z0W16r6uJsMdZ9r6MP/AfBOC1UvsbQyfbgllkCuMEdqvfHcmBSe7t+bbQYl4oovIlQ==",
-      "license": "GPL3",
-      "dependencies": {
-        "ansi-align": "^3.0.1",
-        "ansi-escapes": "^7.2.0",
-        "boxen": "^8.0.1",
-        "chalk": "^5.6.2",
-        "chalk-string": "^4.0.0",
-        "change-case": "^5.4.4",
-        "cli-highlight": "^2.1.11",
-        "cli-table3": "^0.6.5",
-        "color-namer": "^1.4.0",
-        "compose-function": "^3.0.3",
-        "concat-stream": "^2.0.0",
-        "env-paths": "^3.0.0",
-        "he": "^1.2.0",
-        "inline-style-parser": "^0.2.7",
-        "languages-aliases": "^3.0.0",
-        "longest-line": "0.0.3",
-        "normalize-html-whitespace": "^1.0.0",
-        "number-to-alphabet": "^1.0.0",
-        "parse5": "^8.0.0",
-        "romanize": "^1.1.1",
-        "supports-hyperlinks": "^4.3.0",
-        "terminal-size": "^4.0.0",
-        "wrap-ansi": "^9.0.2",
-        "yaml": "^2.8.1"
-      },
-      "bin": {
-        "html": "bin/html.js"
-      },
-      "engines": {
-        "node": ">=18.0.0",
-        "npm": ">=8.0.0"
-      }
-    },
-    "node_modules/cli-html/node_modules/entities": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
-      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=0.12"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/cli-html/node_modules/has-flag": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz",
-      "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/cli-html/node_modules/parse5": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
-      "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
-      "license": "MIT",
-      "dependencies": {
-        "entities": "^6.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/inikulin/parse5?sponsor=1"
-      }
-    },
-    "node_modules/cli-html/node_modules/supports-color": {
-      "version": "10.2.2",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
-      "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-color?sponsor=1"
-      }
-    },
-    "node_modules/cli-html/node_modules/supports-hyperlinks": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz",
-      "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==",
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^5.0.1",
-        "supports-color": "^10.2.2"
-      },
-      "engines": {
-        "node": ">=20"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
-      }
-    },
-    "node_modules/cli-markdown": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/cli-markdown/-/cli-markdown-3.5.1.tgz",
-      "integrity": "sha512-4WFct6LuFxibmSvAXJTGTxZaVqT14jQ8x58lNXKk6afPgWZEFIkUVTCcEn2afBOb7q2bpGIvTRM58e7WcOLSyQ==",
-      "license": "GPL-3.0-or-later",
-      "dependencies": {
-        "@mdit/plugin-alert": "^0.22.1",
-        "@types/marked": "^6.0.0",
-        "cli-html": "^4.4.0",
-        "concat-stream": "^2.0.0",
-        "markdown-it": "^14.1.0",
-        "markdown-it-abbr": "^2.0.0",
-        "markdown-it-container": "^4.0.0",
-        "markdown-it-deflist": "^3.0.0",
-        "markdown-it-footnote": "^4.0.0",
-        "markdown-it-ins": "^4.0.0",
-        "markdown-it-mark": "^4.0.0",
-        "markdown-it-sub": "^2.0.0",
-        "markdown-it-sup": "^2.0.0",
-        "markdown-it-task-lists": "^2.1.1"
-      },
-      "bin": {
-        "markdown": "bin/markdown.js",
-        "md": "bin/markdown.js"
-      },
-      "engines": {
-        "node": ">=16.0.0",
-        "npm": ">=7.0.0"
-      }
-    },
-    "node_modules/cli-spinners": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz",
-      "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18.20"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/cli-table3": {
-      "version": "0.6.5",
-      "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
-      "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
-      "license": "MIT",
-      "dependencies": {
-        "string-width": "^4.2.0"
-      },
-      "engines": {
-        "node": "10.* || >= 12.*"
-      },
-      "optionalDependencies": {
-        "@colors/colors": "1.5.0"
-      }
-    },
-    "node_modules/cli-table3/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cli-table3/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "license": "MIT"
-    },
-    "node_modules/cli-table3/node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cli-table3/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cli-table3/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cli-truncate": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
-      "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
-      "license": "MIT",
-      "dependencies": {
-        "slice-ansi": "^5.0.0",
-        "string-width": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/cli-truncate/node_modules/slice-ansi": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
-      "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^6.0.0",
-        "is-fullwidth-code-point": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
-      }
-    },
-    "node_modules/cliui": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
-      "license": "ISC",
-      "dependencies": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.0",
-        "wrap-ansi": "^7.0.0"
-      }
-    },
-    "node_modules/cliui/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/cliui/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "license": "MIT"
-    },
-    "node_modules/cliui/node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui/node_modules/wrap-ansi": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/code-excerpt": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz",
-      "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==",
-      "license": "MIT",
-      "dependencies": {
-        "convert-to-spaces": "^2.0.1"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      }
-    },
-    "node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
-      }
-    },
-    "node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "license": "MIT"
-    },
-    "node_modules/color-namer": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/color-namer/-/color-namer-1.4.0.tgz",
-      "integrity": "sha512-3mQMY9MJyfdV2uhe+xjQWcKHtYnPtl5svGjt89V2WWT2MlaLAd7C02886Wq7H1MTjjIIEa/NJLYPNF/Lhxhq2A==",
-      "license": "MIT",
-      "dependencies": {
-        "chroma-js": "^1.3.4",
-        "es6-weak-map": "^2.0.3"
-      }
-    },
-    "node_modules/colors-option": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/colors-option/-/colors-option-6.1.2.tgz",
-      "integrity": "sha512-y23/k7HnkOTebqESoWtgu3Onrc2vydR0jn2kgNSlxLVBC1z2LUYhRadgUcfSgTWUXSpL2ONqUtePB4CCLTyVyg==",
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^5.4.1",
-        "is-plain-obj": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=18.18.0"
-      }
-    },
-    "node_modules/compose-function": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz",
-      "integrity": "sha512-xzhzTJ5eC+gmIzvZq+C3kCJHsp9os6tJkrigDRZclyGtOKINbZtE8n1Tzmeh32jW+BUDPbvZpibwvJHBLGMVwg==",
-      "license": "MIT",
-      "dependencies": {
-        "arity-n": "^1.0.4"
-      }
-    },
-    "node_modules/concat-stream": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
-      "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
-      "engines": [
-        "node >= 6.0"
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^3.0.2",
-        "typedarray": "^0.0.6"
-      }
-    },
-    "node_modules/convert-to-spaces": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz",
-      "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==",
-      "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      }
-    },
-    "node_modules/csstype": {
-      "version": "3.2.3",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
-      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/d": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
-      "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
-      "license": "ISC",
-      "dependencies": {
-        "es5-ext": "^0.10.64",
-        "type": "^2.7.2"
-      },
-      "engines": {
-        "node": ">=0.12"
-      }
-    },
-    "node_modules/deepmerge": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
-      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/emoji-regex": {
-      "version": "10.6.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
-      "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
-      "license": "MIT"
-    },
-    "node_modules/emojilib": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
-      "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==",
-      "license": "MIT"
-    },
-    "node_modules/entities": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
-      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=0.12"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/env-paths": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
-      "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
-      "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/environment": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
-      "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/es-toolkit": {
-      "version": "1.43.0",
-      "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.43.0.tgz",
-      "integrity": "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==",
-      "license": "MIT",
-      "workspaces": [
-        "docs",
-        "benchmarks"
-      ]
-    },
-    "node_modules/es5-ext": {
-      "version": "0.10.64",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
-      "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
-      "hasInstallScript": true,
-      "license": "ISC",
-      "dependencies": {
-        "es6-iterator": "^2.0.3",
-        "es6-symbol": "^3.1.3",
-        "esniff": "^2.0.1",
-        "next-tick": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/es6-iterator": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-      "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-      "license": "MIT",
-      "dependencies": {
-        "d": "1",
-        "es5-ext": "^0.10.35",
-        "es6-symbol": "^3.1.1"
-      }
-    },
-    "node_modules/es6-symbol": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
-      "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
-      "license": "ISC",
-      "dependencies": {
-        "d": "^1.0.2",
-        "ext": "^1.7.0"
-      },
-      "engines": {
-        "node": ">=0.12"
-      }
-    },
-    "node_modules/es6-weak-map": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
-      "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
-      "license": "ISC",
-      "dependencies": {
-        "d": "1",
-        "es5-ext": "^0.10.46",
-        "es6-iterator": "^2.0.3",
-        "es6-symbol": "^3.1.1"
-      }
-    },
-    "node_modules/escalade": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
-      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/escape-string-regexp": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
-      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/esniff": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
-      "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
-      "license": "ISC",
-      "dependencies": {
-        "d": "^1.0.1",
-        "es5-ext": "^0.10.62",
-        "event-emitter": "^0.3.5",
-        "type": "^2.7.2"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/event-emitter": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
-      "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
-      "license": "MIT",
-      "dependencies": {
-        "d": "1",
-        "es5-ext": "~0.10.14"
-      }
-    },
-    "node_modules/ext": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
-      "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
-      "license": "ISC",
-      "dependencies": {
-        "type": "^2.7.2"
-      }
-    },
-    "node_modules/figures": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
-      "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
-      "license": "MIT",
-      "dependencies": {
-        "is-unicode-supported": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "license": "ISC",
-      "engines": {
-        "node": "6.* || 8.* || >= 10.*"
-      }
-    },
-    "node_modules/get-east-asian-width": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
-      "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/he": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
-      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
-      "license": "MIT",
-      "bin": {
-        "he": "bin/he"
-      }
-    },
-    "node_modules/highlight.js": {
-      "version": "10.7.3",
-      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
-      "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/indent-string": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
-      "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "license": "ISC"
-    },
-    "node_modules/ink": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/ink/-/ink-5.2.1.tgz",
-      "integrity": "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg==",
-      "license": "MIT",
-      "dependencies": {
-        "@alcalzone/ansi-tokenize": "^0.1.3",
-        "ansi-escapes": "^7.0.0",
-        "ansi-styles": "^6.2.1",
-        "auto-bind": "^5.0.1",
-        "chalk": "^5.3.0",
-        "cli-boxes": "^3.0.0",
-        "cli-cursor": "^4.0.0",
-        "cli-truncate": "^4.0.0",
-        "code-excerpt": "^4.0.0",
-        "es-toolkit": "^1.22.0",
-        "indent-string": "^5.0.0",
-        "is-in-ci": "^1.0.0",
-        "patch-console": "^2.0.0",
-        "react-reconciler": "^0.29.0",
-        "scheduler": "^0.23.0",
-        "signal-exit": "^3.0.7",
-        "slice-ansi": "^7.1.0",
-        "stack-utils": "^2.0.6",
-        "string-width": "^7.2.0",
-        "type-fest": "^4.27.0",
-        "widest-line": "^5.0.0",
-        "wrap-ansi": "^9.0.0",
-        "ws": "^8.18.0",
-        "yoga-layout": "~3.2.1"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "@types/react": ">=18.0.0",
-        "react": ">=18.0.0",
-        "react-devtools-core": "^4.19.1"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        },
-        "react-devtools-core": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/inline-style-parser": {
-      "version": "0.2.7",
-      "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
-      "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
-      "license": "MIT"
-    },
-    "node_modules/is-fullwidth-code-point": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
-      "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-in-ci": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz",
-      "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==",
-      "license": "MIT",
-      "bin": {
-        "is-in-ci": "cli.js"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-plain-obj": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
-      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-unicode-supported": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
-      "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "license": "MIT"
-    },
-    "node_modules/languages-aliases": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/languages-aliases/-/languages-aliases-3.0.0.tgz",
-      "integrity": "sha512-0TeT8ZQXq5y59hzowc2PSHkqDDEApXUpHl35BmwxG+8Uuy30ISpMMx51kdBHD3LoVmGgNxBsKcsV4HJClL4U2g==",
-      "license": "GPL-3.0"
-    },
-    "node_modules/linkify-it": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
-      "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
-      "license": "MIT",
-      "dependencies": {
-        "uc.micro": "^2.0.0"
-      }
-    },
-    "node_modules/longest-line": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/longest-line/-/longest-line-0.0.3.tgz",
-      "integrity": "sha512-ZRdPmYYhydc50iw8abrurJaWD+MGLaMOZrNOE2BzQIsumYBIbg07ByILhyiSXIczVdBxipotSrbdgt2JjWPeCg==",
-      "license": "MIT",
-      "dependencies": {
-        "strip-ansi": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/longest-line/node_modules/ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/longest-line/node_modules/strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/loose-envify": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
-      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
-      "license": "MIT",
-      "dependencies": {
-        "js-tokens": "^3.0.0 || ^4.0.0"
-      },
-      "bin": {
-        "loose-envify": "cli.js"
-      }
-    },
-    "node_modules/markdown-it": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
-      "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1",
-        "entities": "^4.4.0",
-        "linkify-it": "^5.0.0",
-        "mdurl": "^2.0.0",
-        "punycode.js": "^2.3.1",
-        "uc.micro": "^2.1.0"
-      },
-      "bin": {
-        "markdown-it": "bin/markdown-it.mjs"
-      }
-    },
-    "node_modules/markdown-it-abbr": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-abbr/-/markdown-it-abbr-2.0.0.tgz",
-      "integrity": "sha512-of7C8pXSjXjDojW4neNP+jD7inUYH/DO0Ca+K/4FUEccg0oHAEX/nfscw0jfz66PJbYWOAT9U8mjO21X5p6aAw==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-container": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-4.0.0.tgz",
-      "integrity": "sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-deflist": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-deflist/-/markdown-it-deflist-3.0.0.tgz",
-      "integrity": "sha512-OxPmQ/keJZwbubjiQWOvKLHwpV2wZ5I3Smc81OjhwbfJsjdRrvD5aLTQxmZzzePeO0kbGzAo3Krk4QLgA8PWLg==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-footnote": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-4.0.0.tgz",
-      "integrity": "sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-ins": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-4.0.0.tgz",
-      "integrity": "sha512-sWbjK2DprrkINE4oYDhHdCijGT+MIDhEupjSHLXe5UXeVr5qmVxs/nTUVtgi0Oh/qtF+QKV0tNWDhQBEPxiMew==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-mark": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-4.0.0.tgz",
-      "integrity": "sha512-YLhzaOsU9THO/cal0lUjfMjrqSMPjjyjChYM7oyj4DnyaXEzA8gnW6cVJeyCrCVeyesrY2PlEdUYJSPFYL4Nkg==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-sub": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-2.0.0.tgz",
-      "integrity": "sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-sup": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-2.0.0.tgz",
-      "integrity": "sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA==",
-      "license": "MIT"
-    },
-    "node_modules/markdown-it-task-lists": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
-      "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==",
-      "license": "ISC"
-    },
-    "node_modules/marked": {
-      "version": "15.0.12",
-      "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
-      "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
-      "license": "MIT",
-      "bin": {
-        "marked": "bin/marked.js"
-      },
-      "engines": {
-        "node": ">= 18"
-      }
-    },
-    "node_modules/marked-terminal": {
-      "version": "7.3.0",
-      "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz",
-      "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-escapes": "^7.0.0",
-        "ansi-regex": "^6.1.0",
-        "chalk": "^5.4.1",
-        "cli-highlight": "^2.1.11",
-        "cli-table3": "^0.6.5",
-        "node-emoji": "^2.2.0",
-        "supports-hyperlinks": "^3.1.0"
-      },
-      "engines": {
-        "node": ">=16.0.0"
-      },
-      "peerDependencies": {
-        "marked": ">=1 <16"
-      }
-    },
-    "node_modules/mdurl": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
-      "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
-      "license": "MIT"
-    },
-    "node_modules/meow": {
-      "version": "13.2.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
-      "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/mz": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
-      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0",
-        "object-assign": "^4.0.1",
-        "thenify-all": "^1.0.0"
-      }
-    },
-    "node_modules/next-tick": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
-      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
-      "license": "ISC"
-    },
-    "node_modules/node-emoji": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
-      "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
-      "license": "MIT",
-      "dependencies": {
-        "@sindresorhus/is": "^4.6.0",
-        "char-regex": "^1.0.2",
-        "emojilib": "^2.4.0",
-        "skin-tone": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/normalize-html-whitespace": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz",
-      "integrity": "sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/number-to-alphabet": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/number-to-alphabet/-/number-to-alphabet-1.0.0.tgz",
-      "integrity": "sha512-5hahJfMZmQ78ydjHB0d9xrZiUOhEKD1u3BngbhfTs/3CCVbnOlbyAbSw8nIe+dpcRtJ+zOthw2YcG6DkfzlEgA==",
-      "license": "MIT"
-    },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/onetime": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
-      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
-      "license": "MIT",
-      "dependencies": {
-        "mimic-fn": "^2.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/parse5": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
-      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
-      "license": "MIT"
-    },
-    "node_modules/parse5-htmlparser2-tree-adapter": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
-      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
-      "license": "MIT",
-      "dependencies": {
-        "parse5": "^6.0.1"
-      }
-    },
-    "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
-      "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
-      "license": "MIT"
-    },
-    "node_modules/patch-console": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz",
-      "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==",
-      "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      }
-    },
-    "node_modules/punycode.js": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
-      "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/react": {
-      "version": "18.3.1",
-      "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
-      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/react-reconciler": {
-      "version": "0.29.2",
-      "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz",
-      "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0",
-        "scheduler": "^0.23.2"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      },
-      "peerDependencies": {
-        "react": "^18.3.1"
-      }
-    },
-    "node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/readdirp": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
-      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 14.18.0"
-      },
-      "funding": {
-        "type": "individual",
-        "url": "https://paulmillr.com/funding/"
-      }
-    },
-    "node_modules/repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/restore-cursor": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
-      "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
-      "license": "MIT",
-      "dependencies": {
-        "onetime": "^5.1.0",
-        "signal-exit": "^3.0.2"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/romanize": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/romanize/-/romanize-1.1.1.tgz",
-      "integrity": "sha512-DpNfq5KrpHNT60jpyOtPyyZGVGNKoBbaMSkCo3m6Hl+4dLPB4jUR3Gs/Agbdi8dB314jjyQFl2+dRS7rqXy9Lw==",
-      "license": "MIT",
-      "dependencies": {
-        "repeat-string": "^1.6.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/safe-buffer": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/scheduler": {
-      "version": "0.23.2",
-      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
-      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0"
-      }
-    },
-    "node_modules/signal-exit": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-      "license": "ISC"
-    },
-    "node_modules/skin-tone": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
-      "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
-      "license": "MIT",
-      "dependencies": {
-        "unicode-emoji-modifier-base": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/slice-ansi": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
-      "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^6.2.1",
-        "is-fullwidth-code-point": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
-      }
-    },
-    "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
-      "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
-      "license": "MIT",
-      "dependencies": {
-        "get-east-asian-width": "^1.3.1"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/stack-utils": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
-      "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
-      "license": "MIT",
-      "dependencies": {
-        "escape-string-regexp": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/string_decoder": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
-      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
-      "license": "MIT",
-      "dependencies": {
-        "safe-buffer": "~5.2.0"
-      }
-    },
-    "node_modules/string-width": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
-      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^10.3.0",
-        "get-east-asian-width": "^1.0.0",
-        "strip-ansi": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/strip-ansi": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
-      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
-      }
-    },
-    "node_modules/supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/supports-hyperlinks": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
-      "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0",
-        "supports-color": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=14.18"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
-      }
-    },
-    "node_modules/terminal-size": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/terminal-size/-/terminal-size-4.0.0.tgz",
-      "integrity": "sha512-rcdty1xZ2/BkWa4ANjWRp4JGpda2quksXIHgn5TMjNBPZfwzJIgR68DKfSYiTL+CZWowDX/sbOo5ME/FRURvYQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/thenify": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
-      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0"
-      }
-    },
-    "node_modules/thenify-all": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
-      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
-      "license": "MIT",
-      "dependencies": {
-        "thenify": ">= 3.1.0 < 4"
-      },
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/type": {
-      "version": "2.7.3",
-      "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
-      "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
-      "license": "ISC"
-    },
-    "node_modules/type-fest": {
-      "version": "4.41.0",
-      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
-      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=16"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
-      "license": "MIT"
-    },
-    "node_modules/typescript": {
-      "version": "5.9.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
-      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=14.17"
-      }
-    },
-    "node_modules/uc.micro": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
-      "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
-      "license": "MIT"
-    },
-    "node_modules/undici-types": {
-      "version": "6.21.0",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
-      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/unicode-emoji-modifier-base": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
-      "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "license": "MIT"
-    },
-    "node_modules/widest-line": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
-      "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
-      "license": "MIT",
-      "dependencies": {
-        "string-width": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/wrap-ansi": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
-      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^6.2.1",
-        "string-width": "^7.0.0",
-        "strip-ansi": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/ws": {
-      "version": "8.19.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
-      "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": ">=5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/y18n": {
-      "version": "5.0.8",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
-      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "license": "ISC",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yaml": {
-      "version": "2.8.2",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
-      "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
-      "license": "ISC",
-      "bin": {
-        "yaml": "bin.mjs"
-      },
-      "engines": {
-        "node": ">= 14.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/eemeli"
-      }
-    },
-    "node_modules/yargs": {
-      "version": "16.2.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
-      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
-      "license": "MIT",
-      "dependencies": {
-        "cliui": "^7.0.2",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.0",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^20.2.2"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yargs-parser": {
-      "version": "20.2.9",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
-      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
-      "license": "ISC",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yargs/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yargs/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "license": "MIT"
-    },
-    "node_modules/yargs/node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yargs/node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yargs/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/yoga-layout": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
-      "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
-      "license": "MIT"
-    }
-  }
-}

+ 0 - 57
canvas-tui/package.json

@@ -1,57 +0,0 @@
-{
-  "name": "@claude-mods/canvas-tui",
-  "version": "0.1.0",
-  "description": "Terminal canvas for Claude Code - live markdown preview in split panes",
-  "type": "module",
-  "main": "dist/index.js",
-  "bin": {
-    "canvas": "./bin/canvas.js",
-    "canvas-tui": "./bin/canvas.js"
-  },
-  "scripts": {
-    "build": "tsc",
-    "dev": "tsc --watch",
-    "start": "node bin/canvas.js",
-    "test": "node --test"
-  },
-  "keywords": [
-    "claude",
-    "claude-code",
-    "terminal",
-    "tui",
-    "canvas",
-    "markdown",
-    "ink",
-    "react"
-  ],
-  "author": "0xDarkMatter",
-  "license": "MIT",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/0xDarkMatter/claude-mods.git",
-    "directory": "canvas-tui"
-  },
-  "engines": {
-    "node": ">=18.0.0"
-  },
-  "dependencies": {
-    "@inkjs/ui": "^2.0.0",
-    "chalk": "^5.3.0",
-    "chokidar": "^4.0.3",
-    "cli-markdown": "^3.5.1",
-    "ink": "^5.0.1",
-    "marked": "^15.0.4",
-    "marked-terminal": "^7.2.1",
-    "meow": "^13.2.0",
-    "react": "^18.3.1"
-  },
-  "devDependencies": {
-    "@types/node": "^22.10.5",
-    "@types/react": "^18.3.18",
-    "typescript": "^5.7.2"
-  },
-  "files": [
-    "dist",
-    "bin"
-  ]
-}

+ 0 - 437
canvas-tui/src/app.tsx

@@ -1,437 +0,0 @@
-import React, { useState, useEffect, useLayoutEffect } from 'react';
-import { Box, Text, useApp, useInput, useStdout } from 'ink';
-import fs from 'fs';
-import { Header } from './components/Header.js';
-import { MarkdownView } from './components/MarkdownView.js';
-import { StatusBar } from './components/StatusBar.js';
-import { useFileWatcher } from './hooks/useFileWatcher.js';
-import { useDirectoryFiles } from './hooks/useDirectoryFiles.js';
-import { readMeta, type CanvasMeta } from './lib/ipc.js';
-import { editInExternalEditor } from './lib/editor.js';
-
-interface AppProps {
-  watchPath: string;
-  watchDir: string;
-  enableMouse?: boolean;
-}
-
-// ANSI escape sequences for mouse support
-const ENABLE_MOUSE = '\x1B[?1000h\x1B[?1002h\x1B[?1006h';
-const DISABLE_MOUSE = '\x1B[?1000l\x1B[?1002l\x1B[?1006l';
-
-export const App: React.FC<AppProps> = ({ watchPath, watchDir, enableMouse = false }) => {
-  const { exit } = useApp();
-  const { stdout } = useStdout();
-  const [content, setContent] = useState<string>('');
-  const [meta, setMeta] = useState<CanvasMeta | null>(null);
-  const [syncStatus, setSyncStatus] = useState<'waiting' | 'synced' | 'watching'>('waiting');
-  const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
-  const [scrollOffset, setScrollOffset] = useState(0);
-  const [totalLines, setTotalLines] = useState(0);
-  const [mouseEnabled, setMouseEnabled] = useState(enableMouse);
-  const [isEditing, setIsEditing] = useState(false);
-
-  // File selector state
-  const [currentFilePath, setCurrentFilePath] = useState(watchPath);
-  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
-  const [isDropdownFocused, setIsDropdownFocused] = useState(false);
-  const [selectedFileIndex, setSelectedFileIndex] = useState(0);
-
-  // Info overlay state
-  const [isInfoOpen, setIsInfoOpen] = useState(false);
-
-  // Terminal dimensions
-  const rows = stdout?.rows || 24;
-  const cols = stdout?.columns || 80;
-  const contentHeight = rows - 4; // Header (2) + Footer (2)
-
-  // Get files in directory
-  const files = useDirectoryFiles(watchDir);
-
-  // File watcher - watch the current file
-  const { content: watchedContent, error } = useFileWatcher(currentFilePath);
-
-  // Enable mouse tracking
-  useLayoutEffect(() => {
-    if (mouseEnabled) {
-      process.stdout.write(ENABLE_MOUSE);
-
-      // Listen for mouse events on stdin
-      const handleData = (data: Buffer) => {
-        const str = data.toString();
-
-        // SGR mouse format: \x1B[<button;x;yM or \x1B[<button;x;ym
-        // Button 64 = scroll up, Button 65 = scroll down
-        const sgrMatch = str.match(/\x1B\[<(\d+);(\d+);(\d+)([Mm])/);
-        if (sgrMatch) {
-          const button = parseInt(sgrMatch[1], 10);
-          if (button === 64) {
-            // Scroll up
-            setScrollOffset(prev => Math.max(0, prev - 3));
-          } else if (button === 65) {
-            // Scroll down
-            setScrollOffset(prev => Math.min(Math.max(0, totalLines - contentHeight), prev + 3));
-          }
-        }
-      };
-
-      process.stdin.on('data', handleData);
-
-      return () => {
-        process.stdout.write(DISABLE_MOUSE);
-        process.stdin.off('data', handleData);
-      };
-    }
-  }, [mouseEnabled, totalLines, contentHeight]);
-
-  // Update content when file changes
-  useEffect(() => {
-    if (watchedContent !== null) {
-      setContent(watchedContent);
-      setSyncStatus('synced');
-      setLastUpdate(new Date());
-      setScrollOffset(0);
-
-      const metaPath = watchDir + '/meta.json';
-      readMeta(metaPath).then(setMeta).catch(() => {});
-    }
-  }, [watchedContent, watchDir]);
-
-  // Info overlay using ANSI escape codes
-  useLayoutEffect(() => {
-    if (!isInfoOpen || !stdout) return;
-
-    // Get file stats
-    let stats: fs.Stats | null = null;
-    try {
-      stats = fs.statSync(currentFilePath);
-    } catch {
-      // File may not exist yet
-    }
-
-    // Calculate content stats
-    const lineCount = content ? content.split('\n').length : 0;
-    const wordCount = content ? content.split(/\s+/).filter(w => w.length > 0).length : 0;
-    const charCount = content ? content.length : 0;
-
-    // Format dates
-    const formatDate = (date: Date) => {
-      return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
-    };
-
-    const created = stats ? formatDate(stats.birthtime) : 'Unknown';
-    const modified = stats ? formatDate(stats.mtime) : 'Unknown';
-    const fileSize = stats ? `${stats.size} bytes` : 'Unknown';
-
-    // Get filename from path
-    const fileName = currentFilePath.split(/[/\\]/).pop() || 'Unknown';
-
-    // Content lines (without border/padding - we'll add those)
-    const contentLines: string[] = [
-      '\x1B[1mFile Metadata\x1B[0m',
-      '',
-      `Filename:   ${fileName}`,
-      `Created:    ${created}`,
-      `Modified:   ${modified}`,
-      `Size:       ${fileSize}`,
-      '',
-      `Lines:      ${lineCount.toLocaleString()}`,
-      `Words:      ${wordCount.toLocaleString()}`,
-      `Characters: ${charCount.toLocaleString()}`,
-      '',
-      '\x1B[2m[i] Close\x1B[0m',
-    ];
-
-    // Calculate inner content width
-    const innerWidth = Math.max(...contentLines.map(l => l.replace(/\x1B\[[0-9;]*m/g, '').length));
-
-    // Build the full panel with border and padding
-    // Horizontal: 2 space padding, Vertical: 1 space padding
-    const boxWidth = innerWidth + 6; // 2 space padding each side inside border, +2 for border chars
-    const totalWidth = boxWidth + 2; // +2 for outer spacing
-
-    const panelLines: string[] = [];
-
-    // Outer top padding
-    panelLines.push(' '.repeat(totalWidth));
-
-    // Top border: space + ┌ + ─ repeated + ┐ + space
-    panelLines.push(` ┌${'─'.repeat(boxWidth - 2)}┐ `);
-
-    // Inner top padding row (1 line vertical padding)
-    panelLines.push(` │${' '.repeat(boxWidth - 2)}│ `);
-
-    // Content rows with 2-space horizontal padding
-    contentLines.forEach(line => {
-      const plainLen = line.replace(/\x1B\[[0-9;]*m/g, '').length;
-      const rightPad = innerWidth - plainLen;
-      panelLines.push(` │  ${line}${' '.repeat(rightPad)}  │ `);
-    });
-
-    // Inner bottom padding row (1 line vertical padding)
-    panelLines.push(` │${' '.repeat(boxWidth - 2)}│ `);
-
-    // Bottom border
-    panelLines.push(` └${'─'.repeat(boxWidth - 2)}┘ `);
-
-    // Outer bottom padding
-    panelLines.push(' '.repeat(totalWidth));
-
-    // Center the panel
-    const panelHeight = panelLines.length;
-    const startRow = Math.floor((rows - panelHeight) / 2);
-    const startCol = Math.floor((cols - totalWidth) / 2);
-
-    // Guard against re-entry when we write our own overlay
-    let isRenderingOverlay = false;
-
-    // Function to render the overlay
-    const renderOverlay = () => {
-      if (isRenderingOverlay) return;
-      isRenderingOverlay = true;
-
-      let output = '\x1B[s'; // Save cursor
-      panelLines.forEach((line, idx) => {
-        const row = startRow + idx;
-        output += `\x1B[${row};${startCol}H${line}`;
-      });
-      output += '\x1B[u'; // Restore cursor
-      originalWrite.call(process.stdout, output);
-
-      isRenderingOverlay = false;
-    };
-
-    // Intercept stdout.write to repaint overlay after Ink renders
-    const originalWrite = process.stdout.write;
-    process.stdout.write = function(chunk: any, encoding?: any, callback?: any) {
-      const result = originalWrite.call(process.stdout, chunk, encoding, callback);
-      if (!isRenderingOverlay) {
-        setImmediate(renderOverlay);
-      }
-      return result;
-    } as typeof process.stdout.write;
-
-    // Initial render
-    renderOverlay();
-
-    // Cleanup: restore stdout.write and clear the panel area
-    return () => {
-      process.stdout.write = originalWrite;
-
-      let clear = '\x1B[s';
-      panelLines.forEach((_, idx) => {
-        const row = startRow + idx;
-        clear += `\x1B[${row};${startCol}H${' '.repeat(totalWidth)}`;
-      });
-      clear += '\x1B[u';
-      originalWrite.call(process.stdout, clear);
-    };
-  }, [isInfoOpen, currentFilePath, content, stdout, rows, cols]);
-
-  // Keyboard input
-  useInput((input, key) => {
-    // Quit
-    if (input === 'q' || (key.ctrl && input === 'c')) {
-      if (mouseEnabled) {
-        process.stdout.write(DISABLE_MOUSE);
-      }
-      exit();
-    }
-
-    // Other files (excluding current) for dropdown navigation
-    const otherFiles = files.filter(f => f.path !== currentFilePath);
-
-    // Tab - toggle file selector focus
-    if (key.tab) {
-      if (isDropdownFocused) {
-        // Close dropdown and unfocus
-        setIsDropdownFocused(false);
-        setIsDropdownOpen(false);
-      } else {
-        // Focus and open dropdown
-        setIsDropdownFocused(true);
-        setIsDropdownOpen(true);
-        // Start at first file
-        setSelectedFileIndex(0);
-      }
-      return;
-    }
-
-    // Escape - close dropdown or info
-    if (key.escape) {
-      if (isInfoOpen) {
-        setIsInfoOpen(false);
-        return;
-      }
-      if (isDropdownOpen) {
-        setIsDropdownOpen(false);
-        setIsDropdownFocused(false);
-        return;
-      }
-    }
-
-    // When dropdown is open, arrow keys navigate files
-    if (isDropdownOpen) {
-      if (key.upArrow) {
-        setSelectedFileIndex(prev => Math.max(0, prev - 1));
-        return;
-      }
-      if (key.downArrow) {
-        setSelectedFileIndex(prev => Math.min(otherFiles.length - 1, prev + 1));
-        return;
-      }
-      if (key.return && otherFiles[selectedFileIndex]) {
-        // Select file and close dropdown
-        setCurrentFilePath(otherFiles[selectedFileIndex].path);
-        setIsDropdownOpen(false);
-        setIsDropdownFocused(false);
-        setScrollOffset(0);
-        return;
-      }
-    }
-
-    // Scrolling (only when dropdown and info are closed)
-    if (!isInfoOpen) {
-      if (key.upArrow) {
-        setScrollOffset(prev => Math.max(0, prev - 1));
-      }
-      if (key.downArrow) {
-        setScrollOffset(prev => Math.min(Math.max(0, totalLines - contentHeight), prev + 1));
-      }
-      if (key.pageUp) {
-        setScrollOffset(prev => Math.max(0, prev - contentHeight));
-      }
-      if (key.pageDown) {
-        setScrollOffset(prev => Math.min(Math.max(0, totalLines - contentHeight), prev + contentHeight));
-      }
-
-      // Home/End and vim-style navigation
-      if (input === 'g' || key.meta && key.upArrow) {
-        setScrollOffset(0);
-      }
-      if (input === 'G' || key.meta && key.downArrow) {
-        setScrollOffset(Math.max(0, totalLines - contentHeight));
-      }
-    }
-
-    // Refresh
-    if (input === 'r') {
-      setSyncStatus('watching');
-    }
-
-    // Toggle mouse capture
-    if (input === 'm') {
-      setMouseEnabled(prev => {
-        const newValue = !prev;
-        if (newValue) {
-          process.stdout.write(ENABLE_MOUSE);
-        } else {
-          process.stdout.write(DISABLE_MOUSE);
-        }
-        return newValue;
-      });
-    }
-
-    // Toggle info overlay
-    if (input === 'i' && !isEditing && !isDropdownOpen) {
-      setIsInfoOpen(prev => !prev);
-    }
-
-    // Edit in external editor
-    if (input === 'e' && !isEditing && content) {
-      setIsEditing(true);
-      // Disable mouse while editing
-      if (mouseEnabled) {
-        process.stdout.write(DISABLE_MOUSE);
-      }
-      editInExternalEditor(currentFilePath)
-        .then(() => {
-          setIsEditing(false);
-          // Re-enable mouse if it was on
-          if (mouseEnabled) {
-            process.stdout.write(ENABLE_MOUSE);
-          }
-        })
-        .catch(() => {
-          setIsEditing(false);
-          if (mouseEnabled) {
-            process.stdout.write(ENABLE_MOUSE);
-          }
-        });
-    }
-  });
-
-  // Status info
-  const currentFileName = currentFilePath.split(/[/\\]/).pop() || 'file';
-
-  // Format timestamp: DD.MM.YYYY HH:MMam (no seconds)
-  const formatTimestamp = (date: Date) => {
-    const day = String(date.getDate()).padStart(2, '0');
-    const month = String(date.getMonth() + 1).padStart(2, '0');
-    const year = date.getFullYear();
-    let hours = date.getHours();
-    const ampm = hours >= 12 ? 'pm' : 'am';
-    hours = hours % 12 || 12;
-    const minutes = String(date.getMinutes()).padStart(2, '0');
-    return `${day}.${month}.${year} ${hours}:${minutes}${ampm}`;
-  };
-
-  const timestampStr = lastUpdate ? formatTimestamp(lastUpdate) : null;
-
-  // Position indicator: Pos: [001-056/168]
-  const pad3 = (n: number) => String(n).padStart(3, '0');
-  const positionStr = totalLines > 0
-    ? `Pos: [${pad3(scrollOffset + 1)}-${pad3(Math.min(scrollOffset + contentHeight, totalLines))}/${totalLines}]`
-    : null;
-
-  const hints = isEditing
-    ? 'Editing...'
-    : isInfoOpen
-    ? '[i] Close'
-    : isDropdownOpen
-    ? '[Tab] Close [↑↓] Nav [Enter] Open'
-    : '[Tab] Files [i] Info [e] Edit [m] Mouse [q] Quit';
-
-  return (
-    <Box flexDirection="column" height={rows}>
-      <Header
-        title="✿ CANVAS"
-        width={cols}
-        files={files}
-        currentFile={currentFilePath}
-        selectedFileIndex={selectedFileIndex}
-        isDropdownOpen={isDropdownOpen}
-        isDropdownFocused={isDropdownFocused}
-      />
-
-      <Box flexGrow={1} flexDirection="column" overflow="hidden">
-        {content ? (
-          <MarkdownView
-            content={content}
-            scrollOffset={scrollOffset}
-            maxHeight={contentHeight}
-            onLineCount={setTotalLines}
-          />
-        ) : (
-          <Box padding={1}>
-            <Text color="gray">
-              {error ? (
-                <Text color="red">{error}</Text>
-              ) : (
-                `Watching ${watchPath}...\n\nUse /canvas write in Claude Code to send content.`
-              )}
-            </Text>
-          </Box>
-        )}
-      </Box>
-
-      <StatusBar
-        status={syncStatus}
-        timestamp={timestampStr}
-        position={positionStr}
-        hints={hints}
-        width={cols}
-      />
-    </Box>
-  );
-};

+ 0 - 111
canvas-tui/src/components/Header.tsx

@@ -1,111 +0,0 @@
-import React, { useEffect } from 'react';
-import { Box, Text, useStdout } from 'ink';
-import { FileInfo, truncateFilename } from '../hooks/useDirectoryFiles.js';
-
-interface HeaderProps {
-  title: string;
-  width: number;
-  // File selector props
-  files: FileInfo[];
-  currentFile: string;
-  selectedFileIndex: number;
-  isDropdownOpen: boolean;
-  isDropdownFocused: boolean;
-}
-
-const MAX_DISPLAY_LENGTH = 30;
-
-export const Header: React.FC<HeaderProps> = ({
-  title,
-  width,
-  files,
-  currentFile,
-  selectedFileIndex,
-  isDropdownOpen,
-  isDropdownFocused,
-}) => {
-  const { stdout } = useStdout();
-  const leftContent = ` ${title} `;
-
-  // Get current filename from path
-  const currentFileName = currentFile.split(/[/\\]/).pop() || 'No file';
-  const displayName = truncateFilename(currentFileName, MAX_DISPLAY_LENGTH);
-
-  // Filter out current file for dropdown
-  const otherFiles = files.filter(f => f.path !== currentFile);
-
-  // Render dropdown as overlay using ANSI escape codes
-  useEffect(() => {
-    if (!isDropdownOpen || !stdout) return;
-
-    const cols = stdout.columns || 80;
-    const startRow = 3; // Below header border line
-
-    // Build dropdown content (right-aligned)
-    const lines: string[] = [];
-
-    if (otherFiles.length === 0) {
-      lines.push('(no other files)');
-    } else {
-      otherFiles.slice(0, 6).forEach((file, index) => {
-        const isSelected = index === selectedFileIndex;
-        const name = truncateFilename(file.name, MAX_DISPLAY_LENGTH);
-        if (isSelected) {
-          // Reversed video for selected item
-          lines.push(`\x1B[7m ${name} \x1B[0m`);
-        } else {
-          // Dim text for unselected
-          lines.push(`\x1B[2m ${name} \x1B[0m`);
-        }
-      });
-
-      if (otherFiles.length > 6) {
-        lines.push(`\x1B[2m +${otherFiles.length - 6} more \x1B[0m`);
-      }
-    }
-
-    // Find max line length for right-alignment
-    const maxLen = Math.max(...lines.map(l => l.replace(/\x1B\[[0-9;]*m/g, '').length));
-
-    // Save cursor, render dropdown at absolute position (right-aligned), restore cursor
-    let output = '\x1B[s'; // Save cursor position
-
-    lines.forEach((line, idx) => {
-      const row = startRow + idx;
-      const plainLen = line.replace(/\x1B\[[0-9;]*m/g, '').length;
-      const padding = maxLen - plainLen;
-      const startCol = cols - maxLen - 1; // Right margin of 1 (matches header padding)
-      output += `\x1B[${row};${startCol}H${' '.repeat(padding)}${line}`;
-    });
-
-    output += '\x1B[u'; // Restore cursor position
-
-    process.stdout.write(output);
-
-    // Cleanup: clear the dropdown area when closing
-    return () => {
-      let clear = '\x1B[s';
-      lines.forEach((_, idx) => {
-        const row = startRow + idx;
-        const startCol = cols - maxLen - 1;
-        clear += `\x1B[${row};${startCol}H${' '.repeat(maxLen + 1)}`;
-      });
-      clear += '\x1B[u';
-      process.stdout.write(clear);
-    };
-  }, [isDropdownOpen, selectedFileIndex, otherFiles, stdout]);
-
-  return (
-    <Box flexDirection="column">
-      {/* Header bar - always single line with border */}
-      <Box borderStyle="single" borderBottom={true} borderTop={false} borderLeft={false} borderRight={false} borderDimColor>
-        <Box width={width}>
-          <Text bold color="blue">{leftContent}</Text>
-          <Box flexGrow={1} />
-          <Text color="white" dimColor>{displayName} {isDropdownOpen ? '▲' : '▼'}</Text>
-          <Text> </Text>
-        </Box>
-      </Box>
-    </Box>
-  );
-};

+ 0 - 56
canvas-tui/src/components/MarkdownView.tsx

@@ -1,56 +0,0 @@
-import React, { useMemo, useEffect } from 'react';
-import { Box, Text, useStdout } from 'ink';
-import { useMarkdown } from '../hooks/useMarkdown.js';
-
-interface MarkdownViewProps {
-  content: string;
-  scrollOffset: number;
-  maxHeight: number;
-  onLineCount?: (count: number) => void;
-}
-
-export const MarkdownView: React.FC<MarkdownViewProps> = ({
-  content,
-  scrollOffset,
-  maxHeight,
-  onLineCount
-}) => {
-  const { stdout } = useStdout();
-  const width = stdout?.columns || 80;
-  const rendered = useMarkdown(content, width);
-
-  // Split into lines for scrolling
-  const lines = useMemo(() => {
-    return rendered.split('\n');
-  }, [rendered]);
-
-  // Calculate visible window
-  const totalLines = lines.length;
-
-  // Report line count to parent for scroll bounds
-  useEffect(() => {
-    onLineCount?.(totalLines);
-  }, [totalLines, onLineCount]);
-  const clampedOffset = Math.min(scrollOffset, Math.max(0, totalLines - maxHeight));
-  const visibleLines = lines.slice(clampedOffset, clampedOffset + maxHeight);
-
-  // Scroll indicator
-  const showScrollUp = clampedOffset > 0;
-  const showScrollDown = clampedOffset + maxHeight < totalLines;
-
-  return (
-    <Box flexDirection="column" paddingX={1} paddingY={1}>
-      {showScrollUp && (
-        <Text color="gray">--- more above ({clampedOffset} lines) ---</Text>
-      )}
-
-      {visibleLines.map((line, index) => (
-        <Text key={`${clampedOffset}-${index}`}>{line || ' '}</Text>
-      ))}
-
-      {showScrollDown && (
-        <Text color="gray">--- more below ({totalLines - clampedOffset - maxHeight} lines) ---</Text>
-      )}
-    </Box>
-  );
-};

+ 0 - 60
canvas-tui/src/components/StatusBar.tsx

@@ -1,60 +0,0 @@
-import React from 'react';
-import { Box, Text } from 'ink';
-
-interface StatusBarProps {
-  status: 'waiting' | 'synced' | 'watching';
-  timestamp: string | null;
-  position: string | null;
-  hints: string;
-  width: number;
-}
-
-export const StatusBar: React.FC<StatusBarProps> = ({ status, timestamp, position, hints, width }) => {
-  const statusColors: Record<string, string> = {
-    waiting: 'yellow',
-    synced: 'green',
-    watching: 'cyan'
-  };
-
-  const statusIcons: Record<string, string> = {
-    waiting: '...',
-    synced: '***',
-    watching: '...'
-  };
-
-  const statusLabels: Record<string, string> = {
-    waiting: 'Waiting',
-    synced: 'Synced',
-    watching: 'Watching'
-  };
-
-  const statusColor = statusColors[status] || 'white';
-  const statusIcon = statusIcons[status] || ' ';
-  const statusLabel = statusLabels[status] || '';
-
-  // Build left side: [icon] Synced: 09.01.2026 10:18am | Pos: [001-056/168]
-  const leftParts = [`${statusLabel}:`];
-  if (timestamp) leftParts.push(timestamp);
-  if (position) leftParts.push('|', position);
-
-  return (
-    <Box
-      borderStyle="single"
-      borderTop={true}
-      borderBottom={false}
-      borderLeft={false}
-      borderRight={false}
-      borderDimColor
-      flexDirection="column"
-    >
-      <Box width={width} justifyContent="space-between">
-        <Box>
-          <Text color="white" dimColor>[</Text>
-          <Text color={statusColor}>{statusIcon}</Text>
-          <Text color="white" dimColor>] {leftParts.join(' ')}</Text>
-        </Box>
-        <Text color="white" dimColor>{hints}</Text>
-      </Box>
-    </Box>
-  );
-};

+ 0 - 97
canvas-tui/src/hooks/useDirectoryFiles.ts

@@ -1,97 +0,0 @@
-import { useState, useEffect } from 'react';
-import fs from 'fs';
-import path from 'path';
-
-export interface FileInfo {
-  name: string;
-  path: string;
-  mtime: Date;
-}
-
-/**
- * Scans a directory's 'drafts' subdirectory for markdown and text files.
- * Returns sorted list (most recently modified first).
- */
-export function useDirectoryFiles(dirPath: string): FileInfo[] {
-  const [files, setFiles] = useState<FileInfo[]>([]);
-
-  useEffect(() => {
-    const scanDirectory = () => {
-      try {
-        const draftsDir = path.join(dirPath, 'drafts');
-
-        // Create drafts dir if it doesn't exist
-        if (!fs.existsSync(draftsDir)) {
-          fs.mkdirSync(draftsDir, { recursive: true });
-        }
-
-        const entries = fs.readdirSync(draftsDir, { withFileTypes: true });
-        const fileInfos: FileInfo[] = [];
-
-        for (const entry of entries) {
-          if (!entry.isFile()) continue;
-
-          const ext = path.extname(entry.name).toLowerCase();
-          if (ext !== '.md' && ext !== '.txt') continue;
-
-          const filePath = path.join(draftsDir, entry.name);
-          const stats = fs.statSync(filePath);
-
-          fileInfos.push({
-            name: entry.name,
-            path: filePath,
-            mtime: stats.mtime,
-          });
-        }
-
-        // Sort by modified time (most recent first)
-        fileInfos.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
-        setFiles(fileInfos);
-      } catch {
-        setFiles([]);
-      }
-    };
-
-    scanDirectory();
-    const interval = setInterval(scanDirectory, 2000);
-    return () => clearInterval(interval);
-  }, [dirPath]);
-
-  return files;
-}
-
-/**
- * Format relative time for display (e.g., "now", "1h", "3d")
- */
-export function formatRelativeTime(date: Date): string {
-  const now = Date.now();
-  const diff = now - date.getTime();
-
-  const seconds = Math.floor(diff / 1000);
-  const minutes = Math.floor(seconds / 60);
-  const hours = Math.floor(minutes / 60);
-  const days = Math.floor(hours / 24);
-
-  if (seconds < 60) return 'now';
-  if (minutes < 60) return `${minutes}m`;
-  if (hours < 24) return `${hours}h`;
-  if (days < 30) return `${days}d`;
-  return `${Math.floor(days / 30)}mo`;
-}
-
-/**
- * Truncate filename to fit width, preserving extension
- */
-export function truncateFilename(name: string, maxLength: number): string {
-  if (name.length <= maxLength) return name;
-
-  const ext = path.extname(name);
-  const base = path.basename(name, ext);
-  const availableLength = maxLength - ext.length - 3; // -3 for "..."
-
-  if (availableLength <= 0) {
-    return name.slice(0, maxLength - 3) + '...';
-  }
-
-  return base.slice(0, availableLength) + '...' + ext;
-}

+ 0 - 78
canvas-tui/src/hooks/useFileWatcher.ts

@@ -1,78 +0,0 @@
-import { useState, useEffect } from 'react';
-import { watch } from 'chokidar';
-import { readFile } from 'fs/promises';
-import { existsSync } from 'fs';
-
-interface FileWatcherResult {
-  content: string | null;
-  error: string | null;
-  isWatching: boolean;
-}
-
-export function useFileWatcher(filePath: string): FileWatcherResult {
-  const [content, setContent] = useState<string | null>(null);
-  const [error, setError] = useState<string | null>(null);
-  const [isWatching, setIsWatching] = useState(false);
-
-  useEffect(() => {
-    let watcher: ReturnType<typeof watch> | null = null;
-
-    const readContent = async () => {
-      try {
-        if (existsSync(filePath)) {
-          const data = await readFile(filePath, 'utf-8');
-          setContent(data);
-          setError(null);
-        }
-      } catch (err) {
-        setError(`Failed to read file: ${err instanceof Error ? err.message : String(err)}`);
-      }
-    };
-
-    const startWatching = () => {
-      // Initial read
-      readContent();
-
-      // Set up watcher
-      watcher = watch(filePath, {
-        persistent: true,
-        ignoreInitial: false,
-        awaitWriteFinish: {
-          stabilityThreshold: 100,
-          pollInterval: 50
-        }
-      });
-
-      watcher.on('add', () => {
-        readContent();
-        setIsWatching(true);
-      });
-
-      watcher.on('change', () => {
-        readContent();
-      });
-
-      watcher.on('unlink', () => {
-        setContent(null);
-        setError('File was deleted');
-      });
-
-      watcher.on('error', (err: unknown) => {
-        const message = err instanceof Error ? err.message : String(err);
-        setError(`Watcher error: ${message}`);
-      });
-
-      setIsWatching(true);
-    };
-
-    startWatching();
-
-    return () => {
-      if (watcher) {
-        watcher.close();
-      }
-    };
-  }, [filePath]);
-
-  return { content, error, isWatching };
-}

+ 0 - 149
canvas-tui/src/hooks/useMarkdown.ts

@@ -1,149 +0,0 @@
-import { useMemo } from 'react';
-import chalk from 'chalk';
-
-// @ts-ignore - no types for cli-markdown
-import markdown from 'cli-markdown';
-
-export function useMarkdown(content: string, width: number = 80): string {
-  return useMemo(() => {
-    if (!content) return '';
-
-    try {
-      // Normalize line endings (Windows CRLF -> LF)
-      const normalizedContent = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
-
-      // Helper to render inline markdown formatting
-      const renderInline = (text: string): string => {
-        let result = text;
-        // Bold
-        result = result.replace(/\*\*([^*]+)\*\*/g, (_, c) => chalk.bold(c));
-        // Italic
-        result = result.replace(/\*([^*]+)\*/g, (_, c) => chalk.italic(c));
-        // Code
-        result = result.replace(/`([^`]+)`/g, (_, c) => chalk.magenta(c));
-        return result;
-      };
-
-      // Process content in sections - handle numbered lists ourselves, delegate rest to cli-markdown
-      const lines = normalizedContent.split('\n');
-      const outputSections: string[] = [];
-      let currentSection: string[] = [];
-      let inNumberedList = false;
-      let numberedListItems: string[] = [];
-
-      const flushSection = () => {
-        if (currentSection.length > 0) {
-          // Process non-list section with cli-markdown
-          const sectionContent = currentSection.join('\n');
-          if (sectionContent.trim()) {
-            outputSections.push(markdown(sectionContent));
-          }
-          currentSection = [];
-        }
-      };
-
-      const flushNumberedList = () => {
-        if (numberedListItems.length > 0) {
-          // Render numbered list ourselves (blank line before for spacing)
-          const listOutput = numberedListItems.map((item, i) =>
-            `  ${i + 1}. ${renderInline(item)}`
-          ).join('\n');
-          outputSections.push('\n' + listOutput);
-          numberedListItems = [];
-        }
-        inNumberedList = false;
-      };
-
-      for (const rawLine of lines) {
-        const line = rawLine.trimEnd();
-        const numMatch = line.match(/^(\s*)(\d+)\.\s+(.+)$/);
-
-        if (numMatch) {
-          if (!inNumberedList) {
-            flushSection(); // Flush any pending non-list content
-            inNumberedList = true;
-          }
-          numberedListItems.push(numMatch[3]);
-        } else {
-          if (inNumberedList) {
-            flushNumberedList(); // Flush the numbered list
-          }
-          currentSection.push(line);
-        }
-      }
-
-      // Flush any remaining content
-      if (inNumberedList) {
-        flushNumberedList();
-      }
-      flushSection();
-
-      let rendered = outputSections.join('\n');
-
-      // Normalize blank lines - collapse multiple consecutive blank lines into one
-      rendered = rendered.replace(/\n{3,}/g, '\n\n');
-
-      // Post-process headings - cli-markdown doesn't style them properly
-      // Strip ANSI codes for matching, then apply our own styles
-      const stripAnsi = (str: string) => str.replace(/\x1B\[[0-9;]*m/g, '');
-
-      const outputLines = rendered.split('\n');
-      const result: string[] = [];
-      let wasInBulletList = false;
-
-      for (const line of outputLines) {
-        const plain = stripAnsi(line);
-        const isBulletStart = /^\s*[•\-\*]\s/.test(plain) || /^\s*\d+\.\s/.test(plain);
-        const isHeading = /^#{1,6}\s/.test(plain);
-        const isBlankLine = plain.trim() === '';
-
-        // Track if we're in a bullet list context
-        if (isBulletStart) {
-          wasInBulletList = true;
-        }
-
-        // Add blank line when transitioning from bullet list to a heading
-        if (wasInBulletList && isHeading) {
-          result.push('');
-          wasInBulletList = false;
-        }
-
-        // Reset bullet context on blank lines (list has ended)
-        if (isBlankLine) {
-          wasInBulletList = false;
-        }
-
-        // H1: # Heading
-        const h1Match = plain.match(/^# (.+)$/);
-        if (h1Match) {
-          result.push(chalk.bold.blue.underline(h1Match[1]));
-          continue;
-        }
-        // H2: ## Heading
-        const h2Match = plain.match(/^## (.+)$/);
-        if (h2Match) {
-          result.push(chalk.bold.blue(h2Match[1]));
-          continue;
-        }
-        // H3: ### Heading
-        const h3Match = plain.match(/^### (.+)$/);
-        if (h3Match) {
-          result.push(chalk.bold.cyan(h3Match[1]));
-          continue;
-        }
-        // H4+: #### Heading
-        const h4Match = plain.match(/^#{4,} (.+)$/);
-        if (h4Match) {
-          result.push(chalk.bold(h4Match[1]));
-          continue;
-        }
-        result.push(line);
-      }
-
-      return result.join('\n');
-    } catch (err) {
-      console.error('Markdown render error:', err);
-      return content;
-    }
-  }, [content, width]);
-}

+ 0 - 118
canvas-tui/src/index.tsx

@@ -1,118 +0,0 @@
-#!/usr/bin/env node
-import React from 'react';
-import fs from 'fs';
-import path from 'path';
-import { render } from 'ink';
-import meow from 'meow';
-import { App } from './app.js';
-
-const cli = meow(`
-  Usage
-    $ canvas [options]
-
-  Options
-    --watch, -w       Watch directory (default: .claude/canvas)
-    --file, -f        Specific file to watch
-    --mouse, -m       Enable mouse wheel scrolling (default: off)
-    --help            Show this help
-    --version         Show version
-
-  Examples
-    $ canvas                     # Just run it - defaults work
-    $ canvas --file ./draft.md   # Watch specific file
-
-  Controls
-    ↑↓ / Mouse wheel    Scroll content
-    g / G               Top / bottom
-    Tab                 Open file selector
-    e                   Edit in external editor
-    m                   Toggle mouse capture
-    q                   Quit
-
-  Terminal Setup
-    Warp:     Ctrl+Shift+D to split, run 'canvas' in new pane
-    tmux:     tmux split-window -h 'canvas'
-    iTerm2:   Cmd+D to split, run 'canvas' in new pane
-`, {
-  importMeta: import.meta,
-  flags: {
-    watch: {
-      type: 'string',
-      shortFlag: 'w',
-      default: '.claude/canvas'
-    },
-    file: {
-      type: 'string',
-      shortFlag: 'f'
-    },
-    mouse: {
-      type: 'boolean',
-      shortFlag: 'm',
-      default: false
-    }
-  }
-});
-
-// Find canvas directory by searching up from CWD
-function findCanvasDir(startDir: string = process.cwd()): string | null {
-  let current = startDir;
-  const root = path.parse(current).root;
-
-  while (current !== root) {
-    const candidate = path.join(current, '.claude', 'canvas');
-    if (fs.existsSync(candidate)) {
-      return candidate;
-    }
-    current = path.dirname(current);
-  }
-  return null;
-}
-
-// Determine watch directory - find it automatically or use explicit path
-function getWatchDir(explicitPath: string): string {
-  // If user specified absolute path, use it
-  if (path.isAbsolute(explicitPath)) {
-    return explicitPath;
-  }
-
-  // Try to find .claude/canvas by searching up
-  const found = findCanvasDir();
-  if (found) {
-    return found;
-  }
-
-  // Fall back to relative path from CWD
-  return path.resolve(explicitPath);
-}
-
-// Determine initial file to watch
-function getInitialFile(watchDir: string, specificFile?: string): string {
-  if (specificFile) return path.resolve(specificFile);
-
-  // Check drafts directory first
-  const draftsDir = path.join(watchDir, 'drafts');
-  try {
-    if (fs.existsSync(draftsDir)) {
-      const files = fs.readdirSync(draftsDir)
-        .filter(f => f.endsWith('.md') || f.endsWith('.txt'))
-        .sort((a, b) => {
-          const statA = fs.statSync(path.join(draftsDir, a));
-          const statB = fs.statSync(path.join(draftsDir, b));
-          return statB.mtime.getTime() - statA.mtime.getTime();
-        });
-      if (files.length > 0) {
-        return path.join(draftsDir, files[0]);
-      }
-    }
-  } catch {
-    // Fall through to default
-  }
-
-  // Fallback to content.md in watch dir
-  return path.join(watchDir, 'content.md');
-}
-
-const watchDir = getWatchDir(cli.flags.watch);
-const watchPath = getInitialFile(watchDir, cli.flags.file);
-
-render(<App watchPath={watchPath} watchDir={watchDir} enableMouse={cli.flags.mouse} />);

+ 0 - 51
canvas-tui/src/lib/editor.ts

@@ -1,51 +0,0 @@
-import { spawn } from 'child_process';
-import path from 'path';
-
-/**
- * Opens the file in the user's preferred editor and waits for them to close it.
- * Checks $VISUAL, $EDITOR, then falls back to platform defaults.
- */
-export async function editInExternalEditor(filePath: string): Promise<void> {
-  const { cmd, args } = getEditor();
-  const absolutePath = path.resolve(filePath);
-
-  return new Promise((resolve, reject) => {
-    const child = spawn(cmd, [...args, absolutePath], {
-      stdio: 'inherit',
-      shell: true,
-    });
-
-    child.on('error', (err) => {
-      reject(new Error(`Failed to open editor: ${err.message}`));
-    });
-
-    child.on('close', (code) => {
-      if (code === 0) {
-        resolve();
-      } else {
-        reject(new Error(`Editor exited with code ${code}`));
-      }
-    });
-  });
-}
-
-/**
- * Get the editor/opener command to use.
- * Priority: $VISUAL > $EDITOR > platform default (opens with associated app)
- */
-function getEditor(): { cmd: string; args: string[] } {
-  if (process.env.VISUAL) return { cmd: process.env.VISUAL, args: [] };
-  if (process.env.EDITOR) return { cmd: process.env.EDITOR, args: [] };
-
-  // Platform defaults - open with associated application
-  if (process.platform === 'win32') {
-    // 'start' opens with default app, '' is window title, /wait makes it blocking
-    return { cmd: 'start', args: ['""', '/wait'] };
-  }
-  if (process.platform === 'darwin') {
-    // macOS: open -W waits for app to close
-    return { cmd: 'open', args: ['-W'] };
-  }
-  // Linux: xdg-open (doesn't wait, but best we can do)
-  return { cmd: 'xdg-open', args: [] };
-}

+ 0 - 105
canvas-tui/src/lib/ipc.ts

@@ -1,105 +0,0 @@
-import { readFile, writeFile, mkdir } from 'fs/promises';
-import { existsSync } from 'fs';
-import { dirname } from 'path';
-
-export interface CanvasMeta {
-  version: string;
-  contentType: 'email' | 'message' | 'doc';
-  mode: 'view' | 'edit';
-  claudeLastWrite: string | null;
-  userLastEdit: string | null;
-  title?: string;
-}
-
-const DEFAULT_META: CanvasMeta = {
-  version: '1.0',
-  contentType: 'doc',
-  mode: 'view',
-  claudeLastWrite: null,
-  userLastEdit: null
-};
-
-/**
- * Read meta.json from canvas directory
- */
-export async function readMeta(metaPath: string): Promise<CanvasMeta> {
-  try {
-    if (!existsSync(metaPath)) {
-      return DEFAULT_META;
-    }
-    const data = await readFile(metaPath, 'utf-8');
-    return { ...DEFAULT_META, ...JSON.parse(data) };
-  } catch {
-    return DEFAULT_META;
-  }
-}
-
-/**
- * Write meta.json to canvas directory
- */
-export async function writeMeta(metaPath: string, meta: Partial<CanvasMeta>): Promise<void> {
-  const dir = dirname(metaPath);
-  if (!existsSync(dir)) {
-    await mkdir(dir, { recursive: true });
-  }
-
-  const existing = await readMeta(metaPath);
-  const updated: CanvasMeta = {
-    ...existing,
-    ...meta
-  };
-
-  await writeFile(metaPath, JSON.stringify(updated, null, 2), 'utf-8');
-}
-
-/**
- * Read content from canvas content file
- */
-export async function readContent(contentPath: string): Promise<string | null> {
-  try {
-    if (!existsSync(contentPath)) {
-      return null;
-    }
-    return await readFile(contentPath, 'utf-8');
-  } catch {
-    return null;
-  }
-}
-
-/**
- * Write content to canvas content file
- */
-export async function writeContent(contentPath: string, content: string): Promise<void> {
-  const dir = dirname(contentPath);
-  if (!existsSync(dir)) {
-    await mkdir(dir, { recursive: true });
-  }
-  await writeFile(contentPath, content, 'utf-8');
-}
-
-/**
- * Initialize canvas directory with default files
- */
-export async function initCanvas(
-  canvasDir: string,
-  contentType: CanvasMeta['contentType'] = 'doc'
-): Promise<void> {
-  const contentPath = `${canvasDir}/content.md`;
-  const metaPath = `${canvasDir}/meta.json`;
-
-  // Ensure directory exists
-  if (!existsSync(canvasDir)) {
-    await mkdir(canvasDir, { recursive: true });
-  }
-
-  // Create meta file
-  await writeMeta(metaPath, {
-    contentType,
-    claudeLastWrite: new Date().toISOString()
-  });
-
-  // Create empty content file if it doesn't exist
-  if (!existsSync(contentPath)) {
-    await writeContent(contentPath, '');
-  }
-}

+ 0 - 97
canvas-tui/src/lib/templates.ts

@@ -1,97 +0,0 @@
-export interface ContentTemplate {
-  name: string;
-  description: string;
-  template: string;
-}
-
-export const templates: Record<string, ContentTemplate> = {
-  email: {
-    name: 'Email',
-    description: 'Professional email format with subject, greeting, and signature',
-    template: `# Email Draft
-
-\`\`\`
-To:
-CC:
-BCC:
-Subject:
-\`\`\`
-
----
-
-Hi [Name],
-
-[Your message here]
-
-Best regards,
-[Your name]
-
----
-*Draft started: ${new Date().toLocaleString()}*
-`
-  },
-
-  message: {
-    name: 'Message',
-    description: 'Casual message format for Slack, Teams, or Discord',
-    template: `# Message Draft
-
-**To:** #channel / @person
-
----
-
-[Your message here]
-
----
-*Draft started: ${new Date().toLocaleString()}*
-`
-  },
-
-  doc: {
-    name: 'Document',
-    description: 'Structured markdown document with sections',
-    template: `# Document Title
-
-## Overview
-
-[Brief description of the document's purpose]
-
-## Details
-
-[Main content here]
-
-## Summary
-
-[Key takeaways]
-
----
-*Draft started: ${new Date().toLocaleString()}*
-`
-  }
-};
-
-/**
- * Get a template by type, with current timestamp
- */
-export function getTemplate(type: keyof typeof templates): string {
-  const template = templates[type];
-  if (!template) {
-    return templates.doc.template;
-  }
-  // Replace timestamp placeholder with current time
-  return template.template.replace(
-    /\$\{new Date\(\)\.toLocaleString\(\)\}/g,
-    new Date().toLocaleString()
-  );
-}
-
-/**
- * List available template types
- */
-export function listTemplates(): Array<{ type: string; name: string; description: string }> {
-  return Object.entries(templates).map(([type, template]) => ({
-    type,
-    name: template.name,
-    description: template.description
-  }));
-}

+ 0 - 31
canvas-tui/src/types/marked-terminal.d.ts

@@ -1,31 +0,0 @@
-declare module 'marked-terminal' {
-  import { MarkedExtension } from 'marked';
-
-  interface MarkedTerminalOptions {
-    code?: (code: string) => string;
-    blockquote?: (quote: string) => string;
-    html?: (html: string) => string;
-    heading?: (text: string, level: number) => string;
-    firstHeading?: (text: string) => string;
-    hr?: () => string;
-    listitem?: (text: string) => string;
-    list?: (body: string, ordered: boolean) => string;
-    table?: (header: string, body: string) => string;
-    paragraph?: (text: string) => string;
-    strong?: (text: string) => string;
-    em?: (text: string) => string;
-    codespan?: (code: string) => string;
-    del?: (text: string) => string;
-    link?: (href: string, title: string, text: string) => string;
-    href?: (href: string) => string;
-    tableOptions?: {
-      chars?: Record<string, string>;
-    };
-    reflowText?: boolean;
-    width?: number;
-    showSectionPrefix?: boolean;
-    tab?: number;
-  }
-
-  export function markedTerminal(options?: MarkedTerminalOptions): MarkedExtension;
-}

+ 0 - 22
canvas-tui/tsconfig.json

@@ -1,22 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "ES2022",
-    "module": "NodeNext",
-    "moduleResolution": "NodeNext",
-    "lib": ["ES2022"],
-    "outDir": "./dist",
-    "rootDir": "./src",
-    "strict": true,
-    "esModuleInterop": true,
-    "skipLibCheck": true,
-    "forceConsistentCasingInFileNames": true,
-    "declaration": true,
-    "declarationMap": true,
-    "sourceMap": true,
-    "jsx": "react-jsx",
-    "resolveJsonModule": true,
-    "isolatedModules": true
-  },
-  "include": ["src/**/*"],
-  "exclude": ["node_modules", "dist"]
-}

+ 0 - 340
commands/canvas.md

@@ -1,340 +0,0 @@
----
-description: "Terminal canvas for content drafting with live preview. Start split-pane sessions for email, message, and document composition. Triggers on: canvas, draft, compose, write content."
-experimental: true
----
-
-# Canvas - Terminal Content Drafting
-
-> ⚠️ **EXPERIMENTAL** - This feature is under active development. APIs may change. Requires Warp terminal for best experience.
-
-Terminal canvas for interactive content drafting with Claude. Creates a split-pane experience in Warp terminal where Claude writes content and you see live markdown preview.
-
-## Arguments
-
-$ARGUMENTS
-
-- `start [--type email|message|doc]`: Initialize canvas session
-- `write "content"`: Write/update content in canvas
-- `read`: Read current canvas content back
-- `clear`: Clear canvas content
-- `close`: End canvas session and clean up
-
-## Architecture
-
-```
-/canvas <subcommand> [options]
-    │
-    ├─→ /canvas start [--type email|message|doc]
-    │     ├─ Create .claude/canvas/ directory
-    │     ├─ Initialize content.md with template
-    │     ├─ Initialize meta.json with state
-    │     ├─ Detect Warp terminal
-    │     └─ Output setup instructions
-    │
-    ├─→ /canvas write "content"
-    │     ├─ Write content to .claude/canvas/content.md
-    │     ├─ Update meta.json timestamp
-    │     └─ Canvas TUI auto-refreshes
-    │
-    ├─→ /canvas read
-    │     ├─ Read .claude/canvas/content.md
-    │     └─ Return content for Claude to process
-    │
-    ├─→ /canvas clear
-    │     ├─ Clear content.md (keep structure)
-    │     └─ Reset meta.json
-    │
-    └─→ /canvas close
-          ├─ Optional: copy content to clipboard
-          └─ Clean up .claude/canvas/
-```
-
----
-
-## Workflow
-
-### Starting a Canvas Session
-
-```
-User: "Help me draft an email to my manager about the project delay"
-
-Claude: I'll help you draft that email. Starting canvas mode...
-
-[Executes internally:]
-1. mkdir -p .claude/canvas
-2. Write email template to .claude/canvas/content.md
-3. Write meta.json with contentType: "email"
-
-[Output:]
-Canvas initialized with email template.
-
-To see live preview:
-1. Press Cmd+Shift+D (or Ctrl+Shift+D on Windows) to split pane
-2. In the new pane, run: canvas-tui --watch
-```
-
-### Writing Content
-
-```
-[Claude writes the email draft:]
-
-/canvas write "# Email Draft
-
-**To:** manager@company.com
-**Subject:** Project Timeline Update
-
----
-
-Hi Sarah,
-
-I wanted to give you a heads-up about a delay in the Phoenix project...
-
-Best regards,
-[Name]"
-
-[Canvas TUI instantly shows the rendered markdown]
-```
-
-### Reading Edits
-
-```
-User: "I edited the email in the canvas, can you make it more formal?"
-
-[Claude reads current content:]
-/canvas read
-
-[Returns content from .claude/canvas/content.md with user's edits]
-
-[Claude can now rewrite based on user's changes]
-```
-
----
-
-## Execution
-
-### /canvas start
-
-**Step 1: Create IPC Directory**
-
-```bash
-mkdir -p .claude/canvas
-```
-
-**Step 2: Select Template**
-
-Based on `--type` flag (default: doc):
-
-| Type | Template |
-|------|----------|
-| email | Subject line, To/CC fields, greeting, body, signature |
-| message | Casual format for Slack/Teams/Discord |
-| doc | Structured markdown with sections |
-
-**Step 3: Initialize Files**
-
-Write `.claude/canvas/content.md`:
-```markdown
-# Email Draft
-
-**To:**
-**Subject:**
-
----
-
-Hi [Name],
-
-[Your message here]
-
-Best regards,
-[Your name]
-```
-
-Write `.claude/canvas/meta.json`:
-```json
-{
-  "version": "1.0",
-  "contentType": "email",
-  "mode": "view",
-  "claudeLastWrite": "2025-01-08T10:30:00Z",
-  "userLastEdit": null
-}
-```
-
-**Step 4: Output Instructions**
-
-```
-Canvas ready with email template.
-
-Setup (one-time):
-  cd claude-mods/canvas-tui && npm link
-
-To view canvas:
-  1. Split your terminal (Cmd+Shift+D in Warp)
-  2. Run: canvas-tui --watch
-
-I'll write your content and you'll see it update in real-time.
-```
-
-### /canvas write
-
-**Parameters:**
-- Content (required): Markdown string to write
-
-**Execution:**
-
-1. Ensure `.claude/canvas/` exists
-2. Write content to `.claude/canvas/content.md`
-3. Update `meta.json` with `claudeLastWrite` timestamp
-4. Canvas TUI detects change via chokidar and re-renders
-
-**Output:**
-```
-Content updated in canvas.
-```
-
-### /canvas read
-
-**Execution:**
-
-1. Read `.claude/canvas/content.md`
-2. Return content as string
-
-**Use Case:** After user edits content in canvas, Claude reads it back to incorporate changes.
-
-### /canvas clear
-
-**Execution:**
-
-1. Read current `meta.json` to preserve contentType
-2. Write empty template to `content.md`
-3. Reset timestamps in `meta.json`
-
-### /canvas close
-
-**Execution:**
-
-1. Optionally copy final content to clipboard (if requested)
-2. Remove `.claude/canvas/` directory
-3. Confirm cleanup
-
----
-
-## Templates
-
-### Email Template
-
-```markdown
-# Email Draft
-
-**To:**
-**CC:**
-**Subject:**
-
----
-
-Hi [Name],
-
-[Your message here]
-
-Best regards,
-[Your name]
-
----
-*Draft started: {timestamp}*
-```
-
-### Message Template
-
-```markdown
-# Message Draft
-
-**To:** #channel / @person
-
----
-
-[Your message here]
-
----
-*Draft started: {timestamp}*
-```
-
-### Document Template
-
-```markdown
-# Document Title
-
-## Overview
-
-[Brief description]
-
-## Details
-
-[Main content]
-
-## Summary
-
-[Key takeaways]
-
----
-*Draft started: {timestamp}*
-```
-
----
-
-## Integration
-
-### Warp Launch Configuration
-
-Install the launch config for one-click split pane setup:
-
-**Location:** `~/.warp/launch_configurations/claude-canvas.yaml`
-
-```yaml
-name: Claude Canvas
-windows:
-  - tabs:
-      - title: Claude Canvas
-        color: Blue
-        layout:
-          split_direction: vertical
-          panes:
-            - is_focused: true
-            - commands:
-                - exec: "canvas-tui --watch"
-```
-
-**Usage:**
-1. Open Warp Command Palette (Cmd+P)
-2. Search "Claude Canvas"
-3. Select to open split layout
-
-### Canvas TUI Package
-
-```bash
-# Link globally from claude-mods repo
-cd claude-mods/canvas-tui && npm link
-
-# Then use from anywhere
-canvas-tui --watch              # Watch .claude/canvas/content.md
-canvas-tui --file ./draft.md    # Watch specific file
-canvas-tui --help               # Show help
-```
-
----
-
-## File Locations
-
-| File | Purpose |
-|------|---------|
-| `.claude/canvas/content.md` | Shared content file |
-| `.claude/canvas/meta.json` | Session metadata |
-| `~/.warp/launch_configurations/claude-canvas.yaml` | Warp split config |
-
----
-
-## Notes
-
-- Canvas TUI is view-only in MVP; edit mode planned for Phase 2
-- File watching uses chokidar with 100ms debounce
-- Works best with Warp terminal but compatible with any terminal that supports split panes
-- Content persists until `/canvas close` is called

+ 1 - 3
docs/COMMAND-SKILL-PATTERN.md

@@ -16,10 +16,9 @@ Skills get slash-hint discovery via trigger keywords in their description and lo
 Most functionality lives in **skills**, not commands. Only session management and experimental features remain as commands.
 
 ```
-commands/           # Minimal (3 files)
+commands/           # Minimal (2 files)
   sync.md           # Session bootstrap
   save.md           # Session persistence
-  canvas.md         # Experimental TUI
 
 skills/             # Everything else (38 directories)
   explain/
@@ -81,7 +80,6 @@ skills/testgen/
 | Scenario | Use |
 |----------|-----|
 | Session management | Command (sync, save) |
-| Experimental/WIP | Command (canvas) |
 | Everything else | Skill |
 
 ## When to Use Skills

+ 2 - 4
docs/PLAN.md

@@ -14,7 +14,7 @@
 |-----------|-------|-------|
 | Agents | 23 | Domain experts + git-agent background worker |
 | Skills | 64 | Operational skills, CLI tools, workflows, dev tasks |
-| Commands | 3 | Session management (sync, save) + experimental (canvas) |
+| Commands | 2 | Session management (sync, save) |
 | Rules | 5 | CLI tools, thinking, commit style, naming, skill-agent-updates |
 | Output Styles | 4 | Vesper, Spartan, Mentor, Executive |
 | Hooks | 3 | pre-commit-lint, post-edit-format, dangerous-cmd-warn |
@@ -45,9 +45,8 @@
 - [x] Patterns: REST, SQL, security, testing, tailwind
 - [x] Development: explain, spawn, atomise, setperms, introspect, review, testgen
 
-### Commands (3)
+### Commands (2)
 - [x] Session: `/save`, `/sync`
-- [x] Experimental: `/canvas`
 
 ### Documentation
 - [x] ARCHITECTURE.md - Extension system guide with authority levels
@@ -162,7 +161,6 @@ Most commands have been converted to skills for better discovery and on-demand l
 **Remaining as commands:**
 - `/sync` - Session bootstrap (paired with /save)
 - `/save` - Session persistence (paired with /sync)
-- `/canvas` - Experimental (Warp-specific)
 
 ---
 

+ 1 - 1
docs/RESERVED-COMMANDS.md

@@ -47,7 +47,7 @@ Also avoid:
 Current claude-mods skills/commands (verified no conflicts):
 
 **Commands:**
-- atomise, canvas, explain, introspect, save, setperms, spawn, sync
+- atomise, explain, introspect, save, setperms, spawn, sync
 
 **Skills:**
 - review, testgen, code-stats, doc-scanner, file-search, find-replace, git-ops, tool-discovery, task-runner, python-env, structural-search, data-processing, markitdown, etc.

+ 0 - 1
docs/SKILL-SUBAGENT-REFERENCE.md

@@ -111,6 +111,5 @@ hooks:
 |---------|---------|-----------|
 | `/sync` | Main | Must restore session state (tasks, context) |
 | `/save` | Main | Must access current tasks via TaskList |
-| `/canvas` | Main | Interactive TUI requires real-time feedback |
 
 These MUST run in main context - subagent isolation would break their core functionality.