zuzu 0.0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ae81f9e580d8c3e0efd863e9f56b6719a9cd08a092dc6b998ab8bf703ddae000
4
+ data.tar.gz: 287c409a0507b53f2f313da7fb2b00466180680641b36b81cc1d23b9046654db
5
+ SHA512:
6
+ metadata.gz: 500c75552b9483fa2414a17971e3cce3d3c02f12ee0e02f63d723165e7ed1ba9c4b2f75b36d8520d4cbc52127331d11560e2bbca0eca4533e02c426e56e1e597
7
+ data.tar.gz: aa6f574afac309ef301b12632ee791521ee8ec287fcd4d933472c19cd7adc354e4d92c3f42871dc964ae343dde436ce0be01e6e83166ac7784f2389b9c3684db
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abhishek Parolkar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,374 @@
1
+ # Zuzu
2
+
3
+ **Local-first agentic desktop apps with JRuby.**
4
+
5
+ Zuzu is a framework for building privacy-respecting AI desktop assistants that
6
+ run entirely on your machine. No cloud APIs required — your data stays local.
7
+
8
+ <video src="https://raw.githubusercontent.com/parolkar/zuzu/refs/heads/main/docs/demo/zuzu_quick_demo_01.mp4" controls width="100%"></video>
9
+ [Quick Demo](https://raw.githubusercontent.com/parolkar/zuzu/refs/heads/main/docs/demo/zuzu_quick_demo_01.mp4)
10
+
11
+ | Component | Technology |
12
+ |-----------|------------|
13
+ | Runtime | JRuby 10.0.3.0 (Java 21+) |
14
+ | GUI | Glimmer DSL for SWT |
15
+ | Database | SQLite via JDBC |
16
+ | LLM | llamafile (local inference) |
17
+ | Packaging | Warbler (.jar / .war) |
18
+
19
+ ## Quick Start
20
+
21
+ ### Prerequisites
22
+
23
+ - **macOS** or **Linux** (Windows support is planned)
24
+ - **Java 21+**
25
+ - **rbenv** (recommended) or any JRuby version manager
26
+
27
+ ### One-Command Setup
28
+
29
+ ```bash
30
+ git clone https://github.com/parolkar/zuzu.git
31
+ cd zuzu
32
+ bin/setup
33
+ ```
34
+
35
+ `bin/setup` installs Java 21, JRuby 10.0.4.0, and all gem dependencies
36
+ automatically. If you prefer manual setup, see [Manual Setup](#manual-setup)
37
+ below.
38
+
39
+ ### Create Your First App
40
+
41
+ When you run `bin/zuzu new` from the source tree, it automatically detects
42
+ dev mode and points the scaffolded app's Gemfile at your local checkout
43
+ (no published gem needed).
44
+
45
+ ```bash
46
+ bin/zuzu new my_assistant
47
+ cd my_assistant
48
+ bundle install
49
+ ```
50
+
51
+ Download a llamafile model:
52
+
53
+ ```bash
54
+ mkdir -p models
55
+ curl -L -o models/llava-v1.5-7b-q4.llamafile \
56
+ https://huggingface.co/Mozilla/llava-v1.5-7b-llamafile/resolve/main/llava-v1.5-7b-q4.llamafile
57
+ chmod +x models/llava-v1.5-7b-q4.llamafile
58
+ ```
59
+
60
+ Edit `app.rb` to point at your model:
61
+
62
+ ```ruby
63
+ Zuzu.configure do |c|
64
+ c.app_name = 'My Assistant'
65
+ c.llamafile_path = File.expand_path('models/llava-v1.5-7b-q4.llamafile', __dir__)
66
+ c.db_path = File.expand_path('.zuzu/zuzu.db', __dir__)
67
+ c.port = 8080
68
+ end
69
+ ```
70
+
71
+ Launch:
72
+
73
+ ```bash
74
+ # macOS (SWT requires first-thread access):
75
+ JRUBY_OPTS="-J-XstartOnFirstThread -J--enable-native-access=ALL-UNNAMED" bundle exec ruby app.rb
76
+
77
+ # Linux:
78
+ JRUBY_OPTS="-J--enable-native-access=ALL-UNNAMED" bundle exec ruby app.rb
79
+ ```
80
+
81
+ You'll see a native desktop window with a file browser on the left and a chat
82
+ interface on the right. The llamafile model starts automatically in the
83
+ background.
84
+
85
+ ---
86
+
87
+ ## Manual Setup
88
+
89
+ If `bin/setup` doesn't suit your workflow, follow these steps.
90
+
91
+ ### 1. Install Java 21+
92
+
93
+ **macOS (Homebrew):**
94
+
95
+ ```bash
96
+ brew install openjdk@21
97
+ ```
98
+
99
+ Add to your shell profile (`~/.zshrc` or `~/.bash_profile`):
100
+
101
+ ```bash
102
+ export JAVA_HOME=$(/usr/libexec/java_home -v 21)
103
+ export PATH="$JAVA_HOME/bin:$PATH"
104
+ ```
105
+
106
+ **macOS (SDKMAN):**
107
+
108
+ ```bash
109
+ curl -s https://get.sdkman.io | bash
110
+ source "$HOME/.sdkman/bin/sdkman-init.sh"
111
+ sdk install java 21-tem
112
+ ```
113
+
114
+ **Ubuntu / Debian:**
115
+
116
+ ```bash
117
+ sudo apt-get update
118
+ sudo apt-get install openjdk-21-jdk
119
+ ```
120
+
121
+ Verify:
122
+
123
+ ```bash
124
+ java -version
125
+ # → openjdk version "21.x.x" ...
126
+ ```
127
+
128
+ ### 2. Install JRuby 10.0.4.0 via rbenv
129
+
130
+ ```bash
131
+ # Install rbenv if needed
132
+ brew install rbenv ruby-build # macOS
133
+ # or: https://github.com/rbenv/rbenv#installation
134
+
135
+ rbenv install jruby-10.0.4.0
136
+ rbenv local jruby-10.0.4.0
137
+ ruby -v
138
+ # → jruby 10.0.4.0 (ruby 3.x.x) ...
139
+ ```
140
+
141
+ ### 3. Install Gems
142
+
143
+ ```bash
144
+ gem install bundler
145
+ bundle install
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Architecture
151
+
152
+ ```
153
+ ┌────────────────────────────────────────────────────┐
154
+ │ Zuzu App (SWT) │
155
+ │ ┌──────────┐ ┌──────────────────────────────┐ │
156
+ │ │ AgentFS │ │ Chat Interface │ │
157
+ │ │ (files) │ │ │ │
158
+ │ │ │ │ User: How do I ... │ │
159
+ │ │ + docs/ │ │ Zuzu: Let me check ... │ │
160
+ │ │ a.txt │ │ │ │
161
+ │ └──────────┘ └──────────────────────────────┘ │
162
+ │ ┌──────────────────────────────┐ │
163
+ │ │ [ Type a message... ] [Send]│ │
164
+ │ └──────────────────────────────┘ │
165
+ └──────────────────────────┬─────────────────────────┘
166
+
167
+ ┌────────────▼────────────┐
168
+ │ Agent (ReAct) │
169
+ │ ┌─────┐ ┌─────────┐ │
170
+ │ │Tools│ │ Memory │ │
171
+ │ └──┬──┘ └────┬────┘ │
172
+ └──────┼─────────┼───────┘
173
+ │ │
174
+ ┌──────▼─────────▼───────┐
175
+ │ SQLite (JDBC) │
176
+ │ - fs_inodes/dentries │
177
+ │ - messages │
178
+ │ - kv_store │
179
+ │ - tool_calls │
180
+ └────────────────────────┘
181
+
182
+ ┌────────────▼────────────┐
183
+ │ llamafile (local LLM) │
184
+ │ OpenAI-compat API │
185
+ │ http://127.0.0.1:8080 │
186
+ └─────────────────────────┘
187
+ ```
188
+
189
+ ### Project Structure
190
+
191
+ ```
192
+ zuzu/
193
+ ├── lib/zuzu/
194
+ │ ├── config.rb # Zuzu.configure { |c| ... }
195
+ │ ├── store.rb # JDBC SQLite — single connection wrapper
196
+ │ ├── agent_fs.rb # Virtual filesystem + KV store + audit log
197
+ │ ├── memory.rb # Conversation history
198
+ │ ├── llm_client.rb # HTTP client for llamafile
199
+ │ ├── llamafile_manager.rb # Subprocess lifecycle
200
+ │ ├── tool_registry.rb # Register / execute agent tools
201
+ │ ├── agent.rb # ReAct loop (think → act → observe)
202
+ │ ├── app.rb # Glimmer SWT desktop shell
203
+ │ ├── tools/
204
+ │ │ ├── file_tool.rb # read_file, write_file, list_directory
205
+ │ │ ├── shell_tool.rb # run_command (allowlisted)
206
+ │ │ └── web_tool.rb # http_get
207
+ │ └── channels/
208
+ │ ├── base.rb # Abstract channel interface
209
+ │ ├── in_app.rb # Desktop GUI (default)
210
+ │ └── whatsapp.rb # WhatsApp Cloud API webhook
211
+ ├── bin/
212
+ │ ├── zuzu # CLI: new / start / console / version
213
+ │ └── setup # One-command bootstrap
214
+ ├── templates/
215
+ │ └── app.rb # Scaffold template for `zuzu new`
216
+ ├── models/ # Place llamafile models here
217
+ ├── Gemfile
218
+ ├── zuzu.gemspec
219
+ ├── Rakefile
220
+ ├── warble.rb # Warbler config for .jar packaging
221
+ └── app.rb # Phase 1 standalone reference
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Usage Guide
227
+
228
+ ### Custom Tools
229
+
230
+ Register tools that the agent can call during conversations:
231
+
232
+ ```ruby
233
+ Zuzu::ToolRegistry.register(
234
+ 'lookup_weather',
235
+ 'Get current weather for a city.',
236
+ {
237
+ type: 'object',
238
+ properties: { city: { type: 'string' } },
239
+ required: ['city']
240
+ }
241
+ ) do |args, agent_fs|
242
+ # Your logic here — call an API, read a file, etc.
243
+ "Weather in #{args['city']}: 22°C, sunny"
244
+ end
245
+ ```
246
+
247
+ The agent's ReAct loop will automatically discover and call your tools when
248
+ relevant to the user's question.
249
+
250
+ ### WhatsApp Channel
251
+
252
+ Enable WhatsApp so users can chat with your agent via their phone:
253
+
254
+ ```ruby
255
+ Zuzu.configure do |c|
256
+ c.channels = ['whatsapp']
257
+ end
258
+ ```
259
+
260
+ Set environment variables:
261
+
262
+ ```bash
263
+ export WHATSAPP_TOKEN="your_cloud_api_token"
264
+ export WHATSAPP_PHONE_ID="your_phone_number_id"
265
+ export WHATSAPP_PORT=9292 # optional, defaults to 9292
266
+ ```
267
+
268
+ The WhatsApp channel starts a WEBrick webhook server that receives messages
269
+ from the WhatsApp Cloud API and replies using the same agent.
270
+
271
+ ### Virtual Filesystem (AgentFS)
272
+
273
+ The agent operates in a sandboxed SQLite-backed filesystem:
274
+
275
+ ```ruby
276
+ store = Zuzu::Store.new
277
+ fs = Zuzu::AgentFS.new(store)
278
+
279
+ fs.write_file('/notes/todo.txt', 'Buy groceries')
280
+ fs.read_file('/notes/todo.txt') # → "Buy groceries"
281
+ fs.list_dir('/notes') # → ["todo.txt"]
282
+ fs.mkdir('/docs')
283
+ fs.exists?('/notes/todo.txt') # → true
284
+
285
+ # Key-value store
286
+ fs.kv_set('last_query', 'weather in Tokyo')
287
+ fs.kv_get('last_query') # → "weather in Tokyo"
288
+ ```
289
+
290
+ ### Packaging as .jar
291
+
292
+ Use Warbler to create a standalone Java archive:
293
+
294
+ ```bash
295
+ gem install warbler
296
+ warble jar
297
+ java -XstartOnFirstThread -jar zuzu-app.jar # macOS
298
+ java -jar zuzu-app.jar # Linux
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Configuration Reference
304
+
305
+ ```ruby
306
+ Zuzu.configure do |c|
307
+ c.app_name = 'My Agent' # Window title
308
+ c.llamafile_path = 'models/llava-v1.5-7b-q4.llamafile' # Path to llamafile binary
309
+ c.db_path = '.zuzu/zuzu.db' # SQLite database location
310
+ c.port = 8080 # llamafile API port
311
+ c.model = 'LLaMA_CPP' # Model identifier
312
+ c.channels = ['whatsapp'] # Extra channels to enable
313
+ c.log_level = :info # :debug, :info, :warn, :error
314
+ c.window_width = 860 # Window width in pixels
315
+ c.window_height = 620 # Window height in pixels
316
+ end
317
+ ```
318
+
319
+ ---
320
+
321
+ ## CLI Reference
322
+
323
+ ```
324
+ zuzu new APP_NAME Scaffold a new Zuzu application
325
+ zuzu start Launch the Zuzu app in the current directory
326
+ zuzu console Open an IRB session with Zuzu loaded
327
+ zuzu version Print the Zuzu version
328
+ zuzu help Show this message
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Troubleshooting
334
+
335
+ ### macOS: "SWT main thread" error
336
+
337
+ SWT on macOS requires the main thread. Always launch with:
338
+
339
+ ```bash
340
+ JRUBY_OPTS="-J-XstartOnFirstThread -J--enable-native-access=ALL-UNNAMED" bundle exec ruby app.rb
341
+ ```
342
+
343
+ ### llamafile won't start
344
+
345
+ - Ensure the file is executable: `chmod +x models/your-model.llamafile`
346
+ - Check `models/llama.log` for errors.
347
+ - Verify port 8080 isn't already in use: `lsof -i :8080`
348
+
349
+ ### "java.lang.ClassNotFoundException: org.sqlite.JDBC"
350
+
351
+ Run `bundle install` to ensure `jdbc-sqlite3` is installed. The gem must be
352
+ loaded before any database access.
353
+
354
+
355
+ ---
356
+
357
+ ## Philosophy
358
+
359
+ - **Local-first.** Your data never leaves your machine.
360
+ - **Minimal code.** The entire framework is ~800 lines of Ruby.
361
+ - **No Docker.** Just Java, JRuby, and a single SQLite file.
362
+ - **Channels built in.** Every app gets in-app chat and optional WhatsApp.
363
+ - **Tools are Ruby blocks.** No YAML configs, no DSL ceremonies.
364
+
365
+ ---
366
+
367
+ ## License
368
+
369
+ MIT — see [LICENSE](LICENSE).
370
+
371
+ ## Author(s)
372
+
373
+ **Abhishek Parolkar**
374
+ - GitHub: [@parolkar](https://github.com/parolkar)
data/bin/setup ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bash
2
+ # bin/setup — one-command bootstrap for Zuzu development.
3
+ # Installs Java 21, JRuby, and gem dependencies.
4
+ set -euo pipefail
5
+
6
+ RED='\033[0;31m'
7
+ GREEN='\033[0;32m'
8
+ YELLOW='\033[1;33m'
9
+ NC='\033[0m'
10
+
11
+ step() { printf "\n${GREEN}▸ %s${NC}\n" "$1"; }
12
+ warn() { printf "${YELLOW}⚠ %s${NC}\n" "$1"; }
13
+
14
+ JRUBY_VERSION="10.0.3.0"
15
+
16
+ # ── Java 21 ──────────────────────────────────────────────────────
17
+ step "Checking Java 21+"
18
+ if java -version 2>&1 | grep -q 'version "2[1-9]\|version "3'; then
19
+ echo " Java 21+ found."
20
+ else
21
+ warn "Java 21+ not found."
22
+ if [[ "$OSTYPE" == darwin* ]]; then
23
+ step "Installing Java 21 via Homebrew"
24
+ brew install openjdk@21
25
+ echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 21)' >> ~/.zshrc
26
+ export JAVA_HOME=$(/usr/libexec/java_home -v 21)
27
+ export PATH="$JAVA_HOME/bin:$PATH"
28
+ echo " Installed. Restart your shell or run: source ~/.zshrc"
29
+ elif [[ "$OSTYPE" == linux-gnu* ]]; then
30
+ step "Installing Java 21 via apt"
31
+ sudo apt-get update -qq && sudo apt-get install -y openjdk-21-jdk
32
+ else
33
+ echo " Please install Java 21+ manually: https://adoptium.net"
34
+ exit 1
35
+ fi
36
+ fi
37
+
38
+ # ── rbenv + JRuby ────────────────────────────────────────────────
39
+ step "Checking rbenv"
40
+ if ! command -v rbenv &>/dev/null; then
41
+ warn "rbenv not found. Installing..."
42
+ if [[ "$OSTYPE" == darwin* ]]; then
43
+ brew install rbenv ruby-build
44
+ else
45
+ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
46
+ export PATH="$HOME/.rbenv/bin:$PATH"
47
+ fi
48
+ eval "$(rbenv init - bash 2>/dev/null || rbenv init - zsh 2>/dev/null || true)"
49
+ fi
50
+
51
+ step "Checking JRuby $JRUBY_VERSION"
52
+ if rbenv versions --bare 2>/dev/null | grep -q "^jruby-${JRUBY_VERSION}$"; then
53
+ echo " JRuby $JRUBY_VERSION already installed."
54
+ else
55
+ step "Installing JRuby $JRUBY_VERSION (this may take a few minutes)"
56
+ rbenv install --skip-existing "jruby-${JRUBY_VERSION}"
57
+ fi
58
+
59
+ rbenv local "jruby-${JRUBY_VERSION}"
60
+ eval "$(rbenv init - bash 2>/dev/null || rbenv init - zsh 2>/dev/null || true)"
61
+
62
+ # ── Gems ─────────────────────────────────────────────────────────
63
+ step "Installing gem dependencies"
64
+ gem install bundler --conservative
65
+ bundle install
66
+
67
+ # ── Done ─────────────────────────────────────────────────────────
68
+ step "Setup complete!"
69
+ echo ""
70
+ echo " Ruby: $(ruby -v)"
71
+ echo " Java: $(java -version 2>&1 | head -1)"
72
+ echo ""
73
+ echo " Next: Download a llamafile model into models/"
74
+ echo " Then run: bin/zuzu start"
data/bin/zuzu ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env jruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
5
+
6
+ require 'fileutils'
7
+ require 'zuzu'
8
+
9
+ USAGE = <<~USAGE
10
+ Zuzu #{Zuzu::VERSION} — local-first agentic desktop framework
11
+
12
+ Usage:
13
+ zuzu new APP_NAME Scaffold a new Zuzu application
14
+ zuzu start Launch the Zuzu app in the current directory
15
+ zuzu package Package the app as a standalone .jar
16
+ zuzu console Open an IRB session with Zuzu loaded
17
+ zuzu version Print the Zuzu version
18
+ zuzu help Show this message
19
+ USAGE
20
+
21
+ # Detect whether we're running from the zuzu source tree (dev mode)
22
+ # by checking if the gemspec exists alongside this script.
23
+ ZUZU_ROOT = File.expand_path('..', __dir__)
24
+ DEV_MODE = ENV['ZUZU_DEV'] || File.exist?(File.join(ZUZU_ROOT, 'zuzu.gemspec'))
25
+
26
+ command = ARGV.shift
27
+
28
+ case command
29
+ when 'new'
30
+ app_name = ARGV.shift
31
+ abort 'Usage: zuzu new APP_NAME' unless app_name
32
+
33
+ app_dir = File.expand_path(app_name)
34
+ abort "Directory '#{app_name}' already exists." if File.exist?(app_dir)
35
+
36
+ template_src = File.expand_path('../templates/app.rb', __dir__)
37
+
38
+ FileUtils.mkdir_p(app_dir)
39
+ FileUtils.cp(template_src, File.join(app_dir, 'app.rb'))
40
+
41
+ # Generate Gemfile — uses local path when running from source tree,
42
+ # published gem when installed via `gem install zuzu`.
43
+ if DEV_MODE
44
+ zuzu_line = %(gem "zuzu", path: #{ZUZU_ROOT.inspect})
45
+ else
46
+ zuzu_line = %(gem "zuzu")
47
+ end
48
+
49
+ File.write(File.join(app_dir, 'Gemfile'), <<~GEMFILE)
50
+ # frozen_string_literal: true
51
+ source "https://rubygems.org"
52
+ #{zuzu_line}
53
+ GEMFILE
54
+
55
+ puts "Created new Zuzu app: #{app_name}/"
56
+ puts " #{app_name}/app.rb"
57
+ puts " #{app_name}/Gemfile"
58
+ puts ''
59
+ if DEV_MODE
60
+ puts " (dev mode: Gemfile points to #{ZUZU_ROOT})"
61
+ puts ''
62
+ end
63
+ puts 'Next steps:'
64
+ puts " cd #{app_name}"
65
+ puts ' bundle install'
66
+ puts ' # Download a llamafile model into models/'
67
+ puts ' zuzu start'
68
+
69
+ when 'start'
70
+ entry = ['app.rb', 'lib/app.rb'].find { |f| File.exist?(f) }
71
+ abort 'No app.rb found. Run `zuzu start` from your app directory.' unless entry
72
+ load File.expand_path(entry)
73
+
74
+ when 'package'
75
+ entry = ['app.rb', 'lib/app.rb'].find { |f| File.exist?(f) }
76
+ abort 'No app.rb found. Run `zuzu package` from your app directory.' unless entry
77
+
78
+ # Copy warble.rb from zuzu source if not already present
79
+ warble_config = 'warble.rb'
80
+ unless File.exist?(warble_config)
81
+ src = File.expand_path('../warble.rb', __dir__)
82
+ if File.exist?(src)
83
+ FileUtils.cp(src, warble_config)
84
+ puts "Copied warble.rb from zuzu source."
85
+ else
86
+ abort 'warble.rb not found. Please add it to your app directory.'
87
+ end
88
+ end
89
+
90
+ # Ensure warbler gem is available
91
+ warble_bin = `which warble 2>/dev/null`.strip
92
+ if warble_bin.empty?
93
+ puts 'Installing warbler gem...'
94
+ system('gem install warbler') || abort('Failed to install warbler.')
95
+ warble_bin = `which warble 2>/dev/null`.strip
96
+ abort 'warble binary not found after install. Try: gem install warbler' if warble_bin.empty?
97
+ end
98
+
99
+ puts 'Packaging app as zuzu-app.jar ...'
100
+ puts ' (this may take a minute)'
101
+ success = Bundler.with_unbundled_env { system(warble_bin, 'jar') }
102
+ if success
103
+ puts ''
104
+ puts 'Done! Created: zuzu-app.jar'
105
+ puts ''
106
+ puts 'Run it with:'
107
+ puts ' java -XstartOnFirstThread -jar zuzu-app.jar # macOS'
108
+ puts ' java -jar zuzu-app.jar # Linux / Windows'
109
+ else
110
+ abort 'warble jar failed. Check output above for details.'
111
+ end
112
+
113
+ when 'console'
114
+ require 'irb'
115
+ puts "Zuzu #{Zuzu::VERSION} console. Type `exit` to quit."
116
+ IRB.start(__FILE__)
117
+
118
+ when 'version'
119
+ puts "zuzu #{Zuzu::VERSION}"
120
+
121
+ when 'help', nil
122
+ puts USAGE
123
+
124
+ else
125
+ warn "Unknown command: #{command}"
126
+ puts USAGE
127
+ exit 1
128
+ end