<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nadav Avisrur</title>
    <description>The latest articles on DEV Community by Nadav Avisrur (@nadav_avisrur).</description>
    <link>https://dev.to/nadav_avisrur</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3824297%2F97c14a87-ec74-4ee2-ab11-a289ed3d9160.png</url>
      <title>DEV Community: Nadav Avisrur</title>
      <link>https://dev.to/nadav_avisrur</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nadav_avisrur"/>
    <language>en</language>
    <item>
      <title>I gave Claude Code a brain. here's what happened.</title>
      <dc:creator>Nadav Avisrur</dc:creator>
      <pubDate>Mon, 16 Mar 2026 10:09:19 +0000</pubDate>
      <link>https://dev.to/nadav_avisrur/i-gave-claude-code-a-brain-heres-what-happened-3304</link>
      <guid>https://dev.to/nadav_avisrur/i-gave-claude-code-a-brain-heres-what-happened-3304</guid>
      <description>&lt;p&gt;ok so I built something last week. not the point of this post though.&lt;/p&gt;

&lt;p&gt;the point is HOW i built it. because I think I accidentally stumbled into a workflow that's kind of broken.&lt;/p&gt;

&lt;p&gt;I built and shipped a full open source tool, solo, over a weekend. a monorepo with backend, frontend, CLI, landing page, demo videos, marketing content for 6 platforms, SEO, Discord server, security audit, npm packages published. 34 working sessions. one person.&lt;/p&gt;

&lt;p&gt;the tool is &lt;a href="https://github.com/my-claude-utils/clsh" rel="noopener noreferrer"&gt;clsh&lt;/a&gt;. I needed real terminal access on my phone so I built it for myself and open sourced it. but honestly thats not what I want to talk about. I want to talk about the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  the problem with Claude Code sessions
&lt;/h2&gt;

&lt;p&gt;if you've used Claude Code for anything serious, you know the pain. context resets. every new session starts from zero. you explain the same architecture, the same decisions, the same file structure, again and again. you lose momentum. you lose context. and the agent makes dumb mistakes because it doesnt remember what you decided 3 sessions ago.&lt;/p&gt;

&lt;p&gt;I got frustrated enough to try something stupid: what if I gave Claude a brain?&lt;/p&gt;

&lt;h2&gt;
  
  
  obsidian as a context manager
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flg0ky6hp231gcu16zunt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flg0ky6hp231gcu16zunt.png" alt=" " width="470" height="1610"&gt;&lt;/a&gt;&lt;br&gt;
so before I wrote a single line of code, I spent maybe a full day doing deep research. chatgpt, claude, reading docs, looking at how other people structure projects. and then I created an Obsidian vault that mirrors an actual company.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clsh.dev/
  00_Company/         ← identity, vision, mission
  01_RnD/             ← architecture, frontend, backend, infra, devops
  02_Product/          ← MVP definition, features, roadmap, vision
  03_Marketing/        ← social media, content, GTM, branding, SEO
  04_Community/        ← discord, github, contributors, growth
  05_Business/         ← competitors, market intel
  06_Legal/            ← licensing, security, privacy
  Docs/                ← source technical plan
  Handoffs/            ← session handoff notes (43 of them now)
  Templates/           ← reusable note templates
  Execution-Plan.md    ← THE source of truth
  VAULT-INDEX.md       ← navigation hub
  CLAUDE.md            ← agent instructions
  .claude/commands/    ← custom slash commands
  .claude/agents/      ← agent personas (7 of them)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;every folder has an index file. every decision gets logged. every session ends with a handoff. the vault IS the project brain, and Claude Code reads it at the start of every session.&lt;/p&gt;

&lt;h2&gt;
  
  
  the init command
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi07f2m1jdd6hwhcqy694.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi07f2m1jdd6hwhcqy694.png" alt=" " width="800" height="568"&gt;&lt;/a&gt;&lt;br&gt;
this was the most important part. I built a single command that scaffolds this entire vault structure. it creates every folder, every index file, every template, the execution plan skeleton, agent personas, custom commands, everything. one command and you go from empty folder to a fully structured project brain.&lt;/p&gt;

&lt;p&gt;but the real work was before that. I spent hours with ChatGPT and Claude doing deep research on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what departments does a solo dev actually need to think about?&lt;/li&gt;
&lt;li&gt;whats the minimum viable structure for each?&lt;/li&gt;
&lt;li&gt;how should files reference each other (wikilinks)?&lt;/li&gt;
&lt;li&gt;what does a good execution plan look like for a 2-week project?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;that research became the init command. and the init command became the foundation for everything.&lt;/p&gt;
&lt;h2&gt;
  
  
  the workflow commands
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiyf2bq8odinu3zgk550.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiyf2bq8odinu3zgk550.png" alt=" " width="408" height="890"&gt;&lt;/a&gt;&lt;br&gt;
here's where it gets interesting. I wrote 8 custom Claude Code commands that basically turn Obsidian into an operating system for development:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/resume-clsh&lt;/code&gt;&lt;/strong&gt; — reads the execution plan, finds the latest handoff, identifies which steps are unblocked, shows you exactly where you left off and what to do next. every session starts with this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/wrap-up-clsh&lt;/code&gt;&lt;/strong&gt; — updates the execution plan, updates all department files, creates a handoff note with what was done, what's next, what's blocked. every session ends with this. this is what gives the next session its memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/status&lt;/code&gt;&lt;/strong&gt; — full dashboard. progress per phase, department health, social channels, blockers, next milestones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/bug-fix&lt;/code&gt;&lt;/strong&gt; — reads the vault docs, finds relevant code, fixes the bug, updates vault if there's a documentation gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/new-feature&lt;/code&gt;&lt;/strong&gt; — checks the execution plan, creates a feature spec from template, plans the implementation, updates the features list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/sprint&lt;/code&gt;&lt;/strong&gt; — plans the week's work based on whats unblocked in the execution plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/vault-sync&lt;/code&gt;&lt;/strong&gt; — syncs all changed files back to the vault after a batch of work.&lt;/p&gt;

&lt;p&gt;the key insight is that every command reads from AND writes to the vault. so the knowledge compounds. session 34 has the full context of sessions 1-33, not because of some magic memory system, but because every session updated the same files.&lt;/p&gt;
&lt;h2&gt;
  
  
  parallel execution (this is where it gets wild)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tgxh5u5wnknvb6ml4rz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tgxh5u5wnknvb6ml4rz.png" alt=" " width="800" height="563"&gt;&lt;/a&gt;&lt;br&gt;
my execution plan has "parallel groups" built in. steps that dont depend on each other can run simultaneously. so when I hit &lt;code&gt;/resume-clsh&lt;/code&gt;, it doesn't just show me the next step. it shows me ALL unblocked steps and which agent personas should handle each one.&lt;/p&gt;

&lt;p&gt;then I spawn a team. literally. Claude Code creates a team with multiple agents, each working in their own git worktree so they dont step on each other. one agent builds the backend. another builds the frontend. at the same time. then I merge the worktrees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Team: "clsh-phase1b"
+---------------------------+  +---------------------------+
|  backend-engineer          |  |  frontend-engineer         |
|  Step 1.2 + 1.3            |  |  Step 1.4                  |
|  worktree: backend-core    |  |  worktree: frontend-core   |
+---------------------------+  +---------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this is not hypothetical. I have 7 agent personas defined: rnd-lead, frontend-engineer, backend-engineer, devops-engineer, product-manager, marketing-lead, community-lead. each one has a persona file that tells it what it owns, what tools it has, how to coordinate.&lt;/p&gt;

&lt;h2&gt;
  
  
  not just code
&lt;/h2&gt;

&lt;p&gt;and this is the part I think most people miss. this system isn't just for writing code. the vault has marketing, SEO, community, legal, business intel. so when I ran &lt;code&gt;/new-feature&lt;/code&gt; for "create demo video", Claude:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;created a Remotion project (React-based video rendering)&lt;/li&gt;
&lt;li&gt;built 5 animated scenes with iPhone frames&lt;/li&gt;
&lt;li&gt;rendered the video to MP4&lt;/li&gt;
&lt;li&gt;compressed it with ffmpeg&lt;/li&gt;
&lt;li&gt;embedded it in the landing page&lt;/li&gt;
&lt;li&gt;created Instagram composites (1080x1350) with brand colors&lt;/li&gt;
&lt;li&gt;wrote captions for 6 different platforms&lt;/li&gt;
&lt;li&gt;posted to Discord&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;when I ran the SEO workflow, it set up Google Search Console, submitted sitemaps, added meta tags, JSON-LD structured data, Open Graph tags. all tracked in &lt;code&gt;03_Marketing/SEO/SEO.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;when I did the security audit, it found 4 critical and 9 high vulnerabilities, documented them all in &lt;code&gt;01_RnD/Security-Audit.md&lt;/code&gt;, and then I ran fix sessions that checked off each one. the vault tracked the entire thing.&lt;/p&gt;

&lt;p&gt;43 handoff files. each one a snapshot of exactly where things stood at the end of that session.&lt;/p&gt;

&lt;h2&gt;
  
  
  the results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;34 sessions over a weekend&lt;/li&gt;
&lt;li&gt;88% complete (21/24 execution plan steps)&lt;/li&gt;
&lt;li&gt;full monorepo: backend (node-pty + WebSocket + auth), frontend (React + xterm.js), CLI, landing page&lt;/li&gt;
&lt;li&gt;npm published (3 packages), domain live, DNS configured, SSL&lt;/li&gt;
&lt;li&gt;demo videos (Remotion), Instagram content (7 composites), marketing copy for 6 platforms&lt;/li&gt;
&lt;li&gt;Discord server with bot, channels, roles&lt;/li&gt;
&lt;li&gt;security audit + fixes&lt;/li&gt;
&lt;li&gt;SEO infrastructure&lt;/li&gt;
&lt;li&gt;all of this, solo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not saying this is the only way to do it. but the idea of giving your AI coding agent a persistent, structured brain that it reads from and writes to every session... that changed everything for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  want the setup?
&lt;/h2&gt;

&lt;p&gt;I can share the exact Obsidian vault template + all 8 custom commands + the agent personas + the init command. everything you need to replicate this workflow for your own project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;just drop a comment asking for it and I'll DM you the zip.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;or check out &lt;a href="https://github.com/my-claude-utils/clsh" rel="noopener noreferrer"&gt;clsh&lt;/a&gt; if you're curious what I built with it. just a tool I needed for myself and decided to open source.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;built with an unhealthy amount of coffee and an Obsidian vault that knows more about my project than I do&lt;/em&gt;&lt;/p&gt;

</description>
      <category>obsidian</category>
      <category>productivity</category>
      <category>ai</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>How I Built clsh: Phone-First Terminal Access to Your Mac</title>
      <dc:creator>Nadav Avisrur</dc:creator>
      <pubDate>Sat, 14 Mar 2026 17:21:06 +0000</pubDate>
      <link>https://dev.to/nadav_avisrur/how-i-built-clsh-phone-first-terminal-access-to-your-mac-jgb</link>
      <guid>https://dev.to/nadav_avisrur/how-i-built-clsh-phone-first-terminal-access-to-your-mac-jgb</guid>
      <description>&lt;p&gt;I use Claude Code a lot. Not "a few commands a day," full refactoring sessions that run for minutes at a time. And I kept running into the same problem: I'd walk away from my desk, grab my phone, and have zero visibility into what was happening.&lt;/p&gt;

&lt;p&gt;Is it still running? Did it error out? Is it about to overwrite something it shouldn't?&lt;/p&gt;

&lt;p&gt;I tried SSH clients. I tried web-based terminals. Every option had the same flaw: the iOS keyboard. Try typing &lt;code&gt;kubectl get pods -n production --sort-by=.metadata.creationTimestamp&lt;/code&gt; on a touchscreen. It's miserable.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;clsh&lt;/strong&gt;, an open-source tool that gives you real terminal access to your Mac from your phone, with a keyboard that actually works.&lt;/p&gt;

&lt;p&gt;Since it's real PTY sessions, everything that runs in a terminal just works. Claude Code's TUI, Aider's diff view, tmux, vim, git, whatever. This turned out to be one of the most important use cases: with AI coding agents running long sessions, you need a way to monitor and control them from anywhere.&lt;/p&gt;

&lt;p&gt;This is the story of how I built it, the technical decisions I made, and what I learned along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Architecture
&lt;/h3&gt;

&lt;p&gt;clsh has three pieces: an &lt;strong&gt;agent&lt;/strong&gt; that runs on your Mac, a &lt;strong&gt;web frontend&lt;/strong&gt; you open on your phone, and a &lt;strong&gt;tunnel&lt;/strong&gt; connecting them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent is a Node.js process running Express, WebSocket, and node-pty. When you start it, it spawns real PTY sessions. Not &lt;code&gt;child_process.exec&lt;/code&gt;, not Docker containers, actual pseudoterminal sessions attached to your shell.&lt;/p&gt;

&lt;p&gt;I chose node-pty over alternatives because it gives you true PTY semantics: SIGWINCH for resize, proper signal forwarding, and full ANSI escape sequence passthrough. If your terminal can do it locally, clsh can do it remotely.&lt;/p&gt;

&lt;p&gt;Each session gets its own PTY and its own WebSocket connection. The agent tracks sessions in SQLite (WAL mode for concurrent reads) and authenticates requests with JWTs. When tmux is installed, sessions are wrapped in tmux, so they survive server restarts. Close your laptop, reopen, and your sessions are still there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phone Browser  &amp;lt;--&amp;gt;  WebSocket  &amp;lt;--&amp;gt;  node-pty (zsh)
Phone Browser  &amp;lt;--&amp;gt;  WebSocket  &amp;lt;--&amp;gt;  node-pty (tmux)
Phone Browser  &amp;lt;--&amp;gt;  WebSocket  &amp;lt;--&amp;gt;  node-pty (claude)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Tunnel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;clsh has a 3-tier tunnel system. It tries ngrok first (via the Node SDK, not the CLI), falls back to SSH tunneling via localhost.run (no account needed), and finally local WiFi if you're on the same network. When the agent starts, it opens a tunnel and gives you the URL + a QR code. Scan the QR code on your phone and you're connected.&lt;/p&gt;

&lt;p&gt;The nice thing about the SDK approach is that the tunnel lifecycle is managed by the process. Agent starts, tunnel opens. Agent stops, tunnel closes. No dangling processes. The SSH fallback means you can use clsh without any third-party account at all.&lt;/p&gt;

&lt;p&gt;Authentication works via one-time bootstrap tokens. The first time you connect, the agent generates a JWT signed with a secret stored in &lt;code&gt;~/.clsh/jwt_secret&lt;/code&gt;. The token is embedded in the URL. Subsequent connections use the JWT directly (stored in localStorage on the phone).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;React 18 + Vite 6 + xterm.js 5.5 with the WebGL renderer. The frontend is designed phone-first: everything from the layout to the touch handling assumes a phone screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Keyboard Problem (and Solution)
&lt;/h3&gt;

&lt;p&gt;This was the hardest part of the entire project.&lt;/p&gt;

&lt;p&gt;The iOS keyboard is designed for text, not terminals. You don't have Ctrl. You don't have fn. Arrow keys are there but buried. Tab requires switching layouts. Option and Command don't exist. Every terminal session becomes a fight with autocorrect and key prediction.&lt;/p&gt;

&lt;p&gt;My solution was kind of radical: suppress the iOS keyboard entirely and build custom keyboard components for the phone.&lt;/p&gt;

&lt;p&gt;clsh ships with two keyboard layouts. The default is &lt;code&gt;IOSKeyboard&lt;/code&gt;, a 6-row iOS-style layout with oversized letter keys optimized for touch typing. The alternative is &lt;code&gt;MacBookKeyboard&lt;/code&gt;, a pixel-perfect 5-row MacBook layout with the function row, modifier keys, and arrow cluster. Both render every key and map each press to the correct terminal escape sequence. &lt;code&gt;Ctrl+C&lt;/code&gt; sends &lt;code&gt;\x03&lt;/code&gt;. Arrow up sends &lt;code&gt;\x1b[A&lt;/code&gt;. The key sizing follows Apple's proportions, scaled to fit a phone width.&lt;/p&gt;

&lt;p&gt;The tricky part was &lt;strong&gt;modifier keys&lt;/strong&gt;. On a physical keyboard, you hold Ctrl while pressing C. On a touchscreen, you can't hold two keys simultaneously. The solution: sticky modifiers. Tap Ctrl once to activate it (it highlights), tap your target key, and Ctrl automatically deactivates. Tap it twice to lock it on.&lt;/p&gt;

&lt;p&gt;This small UX decision, making modifiers toggle instead of hold, transformed the phone terminal experience from "barely usable" to "surprisingly productive."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Session Grid
&lt;/h3&gt;

&lt;p&gt;When you're running multiple terminal sessions, you need to see them all. clsh shows a tmux-style 2x2 grid where each card displays a miniaturized live preview of the session.&lt;/p&gt;

&lt;p&gt;The previews aren't screenshots. They're generated by reading the xterm.js buffer cell-by-cell (each cell's character, foreground color, and background color) and converting it to colored HTML spans. The result is a tiny, accurate, colorful representation of what each session is doing.&lt;/p&gt;

&lt;p&gt;Tap any card to zoom into full-screen terminal mode. The grid feels like looking at your open MacBook from across the room, and tapping feels like sitting down at it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keyboard Skins (The Fun Part)
&lt;/h3&gt;

&lt;p&gt;I didn't plan to build keyboard skins. It happened because I was staring at the default keyboard and thought "what if it was RGB like a gaming keyboard?"&lt;/p&gt;

&lt;p&gt;clsh ships with six themes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS Terminal&lt;/strong&gt;: The default. 6-row layout with oversized letter keys, optimized for touch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MacBook Silver&lt;/strong&gt;: Clean, minimal, looks like an actual MacBook keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gamer RGB&lt;/strong&gt;: Animated rainbow gradient across the keys. Because why not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Painted&lt;/strong&gt;: Each key a different color, like a paint palette exploded on your keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amber Retro&lt;/strong&gt;: Phosphor terminal aesthetic. Orange keys, dark background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ice White&lt;/strong&gt;: Minimal, high-contrast, all white.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can customize per-key colors and export skins as &lt;code&gt;.kbd&lt;/code&gt; files. The entire skin system uses CSS custom properties, so themes are just variable swaps. Keyboard skins are the mechanical keyboard culture of phone terminals. I didn't expect it to be one of the most satisfying features to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo Mode
&lt;/h3&gt;

&lt;p&gt;The landing page at clsh.dev works without any backend. It runs a demo mode that simulates terminal sessions with realistic typing, ANSI output, and interactive keyboard response.&lt;/p&gt;

&lt;p&gt;Building a convincing demo was important because the entire product is about a &lt;em&gt;feeling&lt;/em&gt;, the feeling of having your MacBook in your pocket. If someone visits the landing page on their phone and the demo makes them go "wait, this is actually usable," the product sells itself.&lt;/p&gt;

&lt;p&gt;The demo auto-types commands at human-like speeds (80-140ms per character for Claude responses, faster for regular shell commands), renders real ANSI color output, and lets you tap the keyboard to see it respond. It runs on a 2-second backend connection timeout. If no backend is reachable, demo mode activates automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;React 18, Vite 6, Tailwind CSS v4&lt;/td&gt;
&lt;td&gt;Fast dev, hot reload, CSS-first config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;xterm.js 5.5, WebGL renderer&lt;/td&gt;
&lt;td&gt;Industry standard, GPU-accelerated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Node.js 20+, Express, ws&lt;/td&gt;
&lt;td&gt;Lightweight, WebSocket-native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PTY&lt;/td&gt;
&lt;td&gt;node-pty&lt;/td&gt;
&lt;td&gt;Real terminal sessions, signal forwarding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tunnel&lt;/td&gt;
&lt;td&gt;ngrok / SSH / WiFi&lt;/td&gt;
&lt;td&gt;3-tier fallback, no account required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;jose (JWT)&lt;/td&gt;
&lt;td&gt;No external dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;better-sqlite3, WAL mode&lt;/td&gt;
&lt;td&gt;Zero config, fast reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monorepo&lt;/td&gt;
&lt;td&gt;Turborepo, npm workspaces&lt;/td&gt;
&lt;td&gt;Parallel builds, shared types&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The whole thing is about 55 source files. The agent is 11 files. The frontend is 44 (components, hooks, lib, demo engine). There's no build step for the landing page, it's static HTML deployed to Cloudflare Pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Learned
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Phone-first is a different design language.&lt;/strong&gt; Everything I know about web development assumes a cursor, a keyboard, and hover states. Designing for touch-only required rethinking every interaction. Buttons need to be bigger. Feedback needs to be immediate and visual (not just hover states). Tap targets need spacing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;xterm.js is incredibly capable.&lt;/strong&gt; The WebGL renderer handles ANSI sequences, 256 colors, mouse events, and cursor positioning flawlessly. The addon ecosystem (fit, web-links, search) covers most needs. The buffer API made the colored preview feature possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sticky modifiers change everything.&lt;/strong&gt; This one UX pattern, making modifier keys toggle instead of hold, is the difference between "this is a toy" and "I can actually work from my phone."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo mode is marketing.&lt;/strong&gt; The best way to sell a developer tool is to let developers use it immediately, for free, with no signup. Demo mode on the landing page does more for conversion than any amount of copy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tmux integration is the secret weapon.&lt;/strong&gt; Wrapping PTY sessions in tmux means they survive server restarts. Close your laptop, reopen it, and your sessions are still running. Combined with a static ngrok URL, this means your phone always connects to the same URL and finds its sessions waiting. It feels like a native app.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;clsh MVP is local-first: your Mac is the server, ngrok is the tunnel, your phone is the client. That's intentional. Zero cloud dependency, total control.&lt;/p&gt;

&lt;p&gt;The roadmap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remote machines&lt;/strong&gt;: Cloud VMs that are always on, accessible from anywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude bootstrap&lt;/strong&gt;: A script that automatically duplicates your local dev environment (dotfiles, repos, configs) to a remote machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams&lt;/strong&gt;: Shared terminal sessions, presence indicators, multiple Claude Code instances working in parallel&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The vision is your development environment as a service. Start solo with your Mac, scale to remote machines, graduate to team workspaces. All from your phone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try It
&lt;/h3&gt;

&lt;p&gt;clsh is MIT licensed and open source. Under a minute to set up, zero config, zero signup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/my-claude-utils/clsh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;clsh
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or try the demo on your phone: &lt;a href="https://clsh.dev" rel="noopener noreferrer"&gt;clsh.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/my-claude-utils/clsh" rel="noopener noreferrer"&gt;github.com/my-claude-utils/clsh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have feedback, ideas, or want to contribute, issues and PRs are open. I'd especially love input on the keyboard UX and what features would make this useful for your workflow.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>terminal</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
