Hotate uses a multi-turn LLM agent capable of calling native tools. This guide explains the architecture, specs, and current limitations so you can understand, fork, and build upon it.
propose_shell_command after the model has enough context. Electron menubar + Vite web shell; planner invoked from CLI and from vite dev middleware at POST /api/plan.menubar. Transparent frameless windows./api/chat). Default model runs entirely offline.fs/promises, child_process, os).fetch('/api/plan') only when you run Vite locally (static hosts serve HTML without that API).| Item | Value / notes |
|---|---|
LLM API |
Ollama POST /api/chat with tools array (native tool calling). Default host http://127.0.0.1:11434. |
Default model |
qwen2.5:0.5b unless overridden by HOTATE_LLM_MODEL. Pull with ollama pull qwen2.5:0.5b. |
Env overrides |
HOTATE_OLLAMA_URL or OLLAMA_HOST for base URL; HOTATE_NUM_PREDICT caps generation length (see cli/lib/generate.mjs → llmConfig()). |
Agent loop |
Up to 7 turns (MAX_TURNS in cli/lib/agent.mjs); temperature 0.1 for the agent chat request. |
Working directory |
CLI and menubar pass a workspace path; read-only tools resolve relative paths against that cwd. Shell proposals are still user-approved before execution. |
| Path | Role |
|---|---|
cli/lib/agent.mjs |
Multi-turn loop: Ollama chat, tool dispatch, final chat vs shell proposal. |
cli/lib/agent-tools.mjs |
Ollama tool schemas + Node implementations (list_directory, read_file_preview, get_disk_space). |
cli/lib/run-pipeline.mjs |
Wires user intent into runAgentLoop and normalizes output for the UI/CLI. |
cli/bin.mjs |
CLI entry used by npm run cmdgen and by Vite’s /api/plan middleware. |
vite.config.js |
Multi-page build (index.html, functions.html, app.html) + dev-only /api/plan bridge. |
menubar/main.cjs |
Electron main: tray window, folder picker, IPC to run the planner against the selected workspace. |
ollama pull qwen2.5:0.5b (or set HOTATE_LLM_MODEL to a model you already have).npm install at the repo root.npm run dev — this repo uses port 3000 by default (vite.config.js). Try-chat and planner need this dev server; a static host serves HTML only unless you wire POST /api/plan yourself.npm run cmdgen with flags you use in CI (see cli/bin.mjs --help).npm run menubar (requires Electron deps installed).qwen2.5:0.5b) is pulled automatically on first launch if missing (one-time download, requires internet). Shipping multi‑GB weights inside the DMG is possible but not wired yet—use CI to attach blobs if you need fully offline installs..dmg fileThe website does not host the DMG for you. It points visitors at the latest GitHub Release that contains a .dmg. You create that file once per version, then publish it.
Option A — Build on your Mac
npm ci at the project root.npm run menubar:pack (this runs bundle:ollama then electron-builder --mac). First run downloads the Ollama darwin bundle (~large).release/ folder. You’ll see something like Hotate-0.1.0-arm64.dmg (name follows package.json version and your CPU arch).Option B — Let GitHub build it (recommended for releases)
version in package.json to the release you want (e.g. 0.1.1).git tag v0.1.1 && git push origin v0.1.1.github/workflows/release-menubar.yml runs on v* tags, builds the DMG on macos-latest, and electron-builder --publish always uploads it to a GitHub Release.public/latest-dmg.js) resolve to that .dmg automatically.If there is no release yet—or the latest release has no .dmg—the button falls back to the releases page so people can still find files manually.
When a user asks Hotate to do something, it doesn't just blindly generate a shell script. Instead, the language model operates in a loop:
User Input -> [LLM Agent]
|
v
Needs Context?
/ \
[YES] [NO]
| |
v v
Call Read Tool Propose Action
(e.g., ls, df) (e.g., rm, mv)
| |
v v
Native Node.js User UI Preview
Tool Execution Accept/Reject
| |
+-------<--------+
(loop)
list_directory(".")propose_shell_command("rm server.log error.log")list_directory). This avoids fragile shell-string parsing and OS inconsistencies.propose_shell_command) to securely queue a command for the user to approve in the UI.| Function | Description | Type |
|---|---|---|
list_directory |
Inspects the filesystem natively to see what files or folders exist before writing commands. | Read-Only |
read_file_preview |
Reads the first N lines of a file to figure out its structure and contents safely. | Read-Only |
get_disk_space |
Checks available and total disk space using native filesystem statistics. | Read-Only |
propose_shell_command |
Proposes a shell command to execute based on intent. Triggers the interactive pre-flight UI before any changes are made. | Action |