Project

zuzu

0.0
No release in over 3 years
Every installed application is an orchestrator of OS capabilities. LLMs are simply a more expressive interface for that orchestration. Zuzu is a framework for building installable, AI-native desktop apps where the intelligence runs on the user's hardware — not in a data center. It uses JRuby and Glimmer DSL for SWT for the GUI, Mozilla's llamafile for local LLM inference, and SQLite (via AgentFS) as a sandboxed virtual filesystem the agent can read and write without touching the host OS. Apps package as a single cross-platform .jar — users download, double-click, run. No cloud. No subscriptions. No infrastructure to operate. Scaffolded projects include CLAUDE.md and Claude Code skills pre-tuned to Zuzu's patterns, so coding agents generate correct framework code from the start.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

Zuzu

Build AI-native desktop apps that run entirely on the user's machine.

Every application you install on an operating system does the same fundamental thing: it translates human intent into OS system calls. A text editor writes bytes to disk. A browser opens network connections. At its core, every installed app is an orchestrator of operating system capabilities.

LLMs are simply a more expressive interface for exactly that orchestration. Zuzu is a framework built on this premise — for developers who want to ship installable, AI-native desktop apps where the intelligence runs on the user's hardware, not in a data center.

Why does this matter?

  • Privacy by architecture. The agent operates inside AgentFS — a sandboxed virtual filesystem backed by a single SQLite file. It cannot touch the host OS unless you explicitly open that door. There is no network call to make, no token to rotate, no terms of service that changes next quarter.

  • Deployable like software, not like a service. Package your app as a single .jar — or go further with zuzu package and produce a native installer (.dmg/.app on macOS, .deb on Linux, .exe on Windows) that bundles a minimal JRE via jlink. Users download, double-click, and run. No Java pre-installed. No Docker. No cloud subscription. No infrastructure to maintain.

  • Built for regulated environments. A therapist keeping session notes, an auditor running confidential analysis, a corporate team in a restricted environment — these are exactly the users who benefit most from powerful AI but are currently blocked by cloud dependency. A bundled LLM in a self-contained Java application needs no external approval to run.

  • Developer experience that matches how software is actually built today. zuzu new my_app scaffolds a project pre-wired for Claude Code: CLAUDE.md, skills for /setup, /add-tool, /customize, and /debug — all enforcing Zuzu's patterns. Open the folder, start your coding agent, describe what you want to build.

Why Zuzu exists · Quick Demo

Quick Demo

Component Technology
Runtime JRuby 10.0.3.0 (Java 21+)
GUI Glimmer DSL for SWT
Database SQLite via JDBC
LLM llamafile (local inference)
Packaging Warbler (.jar / .war)

Quick Start

Prerequisites

  • macOS or Linux (Windows support is planned)
  • Java 21+
  • rbenv (recommended) or any JRuby version manager

One-Command Setup

git clone https://github.com/parolkar/zuzu.git
cd zuzu
bin/setup

bin/setup installs Java 21, JRuby 10.0.3.0, and all gem dependencies automatically. If you prefer manual setup, see Manual Setup below.

Create Your First App

When you run bin/zuzu new from the source tree, it automatically detects dev mode and points the scaffolded app's Gemfile at your local checkout (no published gem needed).

bin/zuzu new my_assistant
cd my_assistant
bundle install

Download a llamafile model:

mkdir -p models
curl -L -o models/llava-v1.5-7b-q4.llamafile \
  https://huggingface.co/Mozilla/llava-v1.5-7b-llamafile/resolve/main/llava-v1.5-7b-q4.llamafile
chmod +x models/llava-v1.5-7b-q4.llamafile

Edit app.rb to point at your model:

Zuzu.configure do |c|
  c.app_name = 'My Assistant'
  # Works both when run directly and from a packaged .jar
  base = __dir__.to_s.start_with?('uri:classloader:') ? Dir.pwd : __dir__
  c.llamafile_path = File.join(base, 'models', 'llava-v1.5-7b-q4.llamafile')
  c.db_path        = File.join(base, '.zuzu', 'zuzu.db')
  c.port           = 8080
end

Launch:

bundle exec zuzu start

You'll see a native desktop chat window. The llamafile model starts automatically in the background. Click Admin Panel to browse the AgentFS virtual filesystem.


Manual Setup

If bin/setup doesn't suit your workflow, follow these steps.

1. Install Java 21+

macOS (Homebrew — recommended):

brew install --cask temurin@21

Temurin is the Eclipse/Adoptium OpenJDK distribution. The cask sets up JAVA_HOME automatically — no manual PATH changes needed.

macOS (SDKMAN):

curl -s https://get.sdkman.io | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 21-tem

Ubuntu / Debian:

sudo apt-get update
sudo apt-get install openjdk-21-jdk

Verify:

java -version
# → openjdk version "21.x.x" ...

2. Install JRuby 10.0.3.0 via rbenv

# Install rbenv if needed
brew install rbenv ruby-build   # macOS
# or: https://github.com/rbenv/rbenv#installation

rbenv install jruby-10.0.3.0
rbenv local jruby-10.0.3.0
ruby -v
# → jruby 10.0.3.0 (ruby 3.x.x) ...

3. Install Gems

gem install bundler
bundle install

Architecture

┌────────────────────────────────────────────────────┐
│                   Zuzu App (SWT)                   │
│  ┌──────────┐  ┌──────────────────────────────┐    │
│  │ AgentFS  │  │       Chat Interface         │    │
│  │  (files) │  │                              │    │
│  │          │  │  User: How do I ...          │    │
│  │  + docs/ │  │  Zuzu: Let me check ...      │    │
│  │    a.txt │  │                              │    │
│  └──────────┘  └──────────────────────────────┘    │
│                ┌──────────────────────────────┐    │
│                │ [  Type a message...  ] [Send]│    │
│                └──────────────────────────────┘    │
└──────────────────────────┬─────────────────────────┘
                           │
              ┌────────────▼────────────┐
              │      Agent (ReAct)      │
              │   ┌─────┐ ┌─────────┐  │
              │   │Tools│ │ Memory  │  │
              │   └──┬──┘ └────┬────┘  │
              └──────┼─────────┼───────┘
                     │         │
              ┌──────▼─────────▼───────┐
              │    SQLite (JDBC)       │
              │  - fs_inodes/dentries  │
              │  - messages            │
              │  - kv_store            │
              │  - tool_calls          │
              └────────────────────────┘
                           │
              ┌────────────▼────────────┐
              │   llamafile (local LLM) │
              │   OpenAI-compat API     │
              │   http://127.0.0.1:8080 │
              └─────────────────────────┘

Project Structure

zuzu/
├── lib/zuzu/
│   ├── config.rb             # Zuzu.configure { |c| ... }
│   ├── store.rb              # JDBC SQLite — single connection wrapper
│   ├── agent_fs.rb           # Virtual filesystem + KV store + audit log
│   ├── memory.rb             # Conversation history
│   ├── llm_client.rb         # HTTP client for llamafile
│   ├── llamafile_manager.rb  # Subprocess lifecycle
│   ├── tool_registry.rb      # Register / execute agent tools
│   ├── agent.rb              # ReAct loop (think → act → observe)
│   ├── app.rb                # Glimmer SWT desktop shell
│   ├── tools/
│   │   ├── file_tool.rb      # read_file, write_file, list_directory
│   │   ├── shell_tool.rb     # run_command (allowlisted)
│   │   └── web_tool.rb       # http_get
│   └── channels/
│       ├── base.rb           # Abstract channel interface
│       ├── in_app.rb         # Desktop GUI (default)
│       └── whatsapp.rb       # WhatsApp Cloud API webhook
├── bin/
│   ├── zuzu                  # CLI: new / start / console / version
│   └── setup                 # One-command bootstrap
├── templates/
│   └── app.rb                # Scaffold template for `zuzu new`
├── models/                   # Place llamafile models here
├── Gemfile
├── zuzu.gemspec
├── Rakefile
├── warble.rb                 # Warbler config for .jar packaging
└── app.rb                    # Phase 1 standalone reference

Usage Guide

Custom Tools

Register tools that the agent can call during conversations:

Zuzu::ToolRegistry.register(
  'lookup_weather',
  'Get current weather for a city.',
  {
    type: 'object',
    properties: { city: { type: 'string' } },
    required: ['city']
  }
) do |args, agent_fs|
  # Your logic here — call an API, read a file, etc.
  "Weather in #{args['city']}: 22°C, sunny"
end

The agent's ReAct loop will automatically discover and call your tools when relevant to the user's question.

WhatsApp Channel

Enable WhatsApp so users can chat with your agent via their phone:

Zuzu.configure do |c|
  c.channels = ['whatsapp']
end

Set environment variables:

export WHATSAPP_TOKEN="your_cloud_api_token"
export WHATSAPP_PHONE_ID="your_phone_number_id"
export WHATSAPP_PORT=9292   # optional, defaults to 9292

The WhatsApp channel starts a WEBrick webhook server that receives messages from the WhatsApp Cloud API and replies using the same agent.

Virtual Filesystem (AgentFS)

The agent operates in a sandboxed SQLite-backed filesystem:

store = Zuzu::Store.new
fs    = Zuzu::AgentFS.new(store)

fs.write_file('/notes/todo.txt', 'Buy groceries')
fs.read_file('/notes/todo.txt')    # → "Buy groceries"
fs.list_dir('/notes')              # → ["todo.txt"]
fs.mkdir('/docs')
fs.exists?('/notes/todo.txt')      # → true

# Key-value store
fs.kv_set('last_query', 'weather in Tokyo')
fs.kv_get('last_query')            # → "weather in Tokyo"

Packaging as .jar

Package your app as a standalone Java archive with a single command:

bundle exec zuzu package

This auto-installs Warbler if needed, generates the necessary launcher, and produces a .jar named after your app directory. Run it with:

java -XstartOnFirstThread -jar my_app.jar   # macOS
java -jar my_app.jar                        # Linux / Windows

Note: Place your llamafile model in a models/ directory alongside the .jar — models are not bundled into the archive.

Packaging as a native installer (no Java required for users)

After building the JAR, zuzu package asks if you want to go further:

Bundle into a self-contained native executable? (no Java required for users) [y/N]:

Type y and Zuzu uses jpackage (bundled with JDK 21+) to produce a native installer that includes a minimal JRE — users never need to install Java:

Platform Output
macOS .dmg with a drag-to-Applications .app
Linux .deb package
Windows .exe installer

The model file is not bundled (it can be gigabytes). After installing, users place it in the platform user-data directory that Zuzu prints at build time:

macOS:   ~/Library/Application Support/<AppName>/models/<model>.llamafile
Linux:   ~/.local/share/<AppName>/models/<model>.llamafile
Windows: %APPDATA%\<AppName>\models\<model>.llamafile

Configuration Reference

Zuzu.configure do |c|
  c.app_name       = 'My Agent'           # Window title
  c.llamafile_path = 'models/llava-v1.5-7b-q4.llamafile'   # Path to llamafile binary
  c.db_path        = '.zuzu/zuzu.db'      # SQLite database location
  c.port           = 8080                  # llamafile API port
  c.model          = 'LLaMA_CPP'          # Model identifier
  c.channels       = ['whatsapp']          # Extra channels to enable
  c.log_level      = :info                 # :debug, :info, :warn, :error
  c.window_width   = 860                   # Window width in pixels
  c.window_height  = 620                   # Window height in pixels
end

CLI Reference

zuzu new APP_NAME    Scaffold a new Zuzu application
zuzu start           Launch the Zuzu app in the current directory
zuzu package         Package the app as a standalone .jar
zuzu console         Open an IRB session with Zuzu loaded
zuzu version         Print the Zuzu version
zuzu help            Show this message

Troubleshooting

macOS: "SWT main thread" error

SWT on macOS requires the main thread. Always launch with:

JRUBY_OPTS="-J-XstartOnFirstThread -J--enable-native-access=ALL-UNNAMED" bundle exec ruby app.rb

llamafile won't start

  • Ensure the file is executable: chmod +x models/your-model.llamafile
  • Check models/llama.log for errors.
  • Verify port 8080 isn't already in use: lsof -i :8080

"java.lang.ClassNotFoundException: org.sqlite.JDBC"

Run bundle install to ensure jdbc-sqlite3 is installed. The gem must be loaded before any database access.


Philosophy

  • Local-first. Your data never leaves your machine.
  • Minimal code. The entire framework is ~800 lines of Ruby.
  • No Docker. Just Java, JRuby, and a single SQLite file.
  • Channels built in. Every app gets in-app chat and optional WhatsApp.
  • Tools are Ruby blocks. No YAML configs, no DSL ceremonies.

License

MIT — see LICENSE.

Author(s)

Abhishek Parolkar