Functions — 機能

Developer Documentation

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.

Fork on GitHub Phase 1: local agent + native read-only tools + guarded shell proposals. Phases 2–4 are planned.

Product roadmap (dev phases)

  1. Phase 1 (current) — Multi-turn Ollama agent with JSON tool calling: list directory, file preview, disk stats, and 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.
  2. Phase 2 (planned) — Richer read-only tools (search, metadata, git status/diff previews), tighter command normalization, and stronger destructive-action classification.
  3. Phase 3 (planned) — Optional cloud-backed model routing and session memory (aligned with the paid tier story on the main site).
  4. Phase 4 (planned) — Remote machine workflows, automation recipes, and long-running task orchestration.

Tech Stack & Architecture

  • Frontend: Plain HTML/CSS/JS bundled with Vite. No React/Vue overhead.
  • Desktop App: Electron with menubar. Transparent frameless windows.
  • LLM Backend: Local Ollama API (/api/chat). Default model runs entirely offline.
  • Native Bridge: Node.js standard libraries (fs/promises, child_process, os).
  • Communication: Electron IPC between renderer and main; the web try-chat uses fetch('/api/plan') only when you run Vite locally (static hosts serve HTML without that API).

Runtime specification

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.mjsllmConfig()).
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.

Repository map (start here)

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.

Run from source (minimal)

  1. Install Ollama and run ollama pull qwen2.5:0.5b (or set HOTATE_LLM_MODEL to a model you already have).
  2. Clone the repo, then npm install at the repo root.
  3. Web + API: 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.
  4. CLI smoke test: npm run cmdgen with flags you use in CI (see cli/bin.mjs --help).
  5. Menubar app: npm run menubar (requires Electron deps installed).
  6. Models: The DMG includes the Ollama runtime (CLI + libraries). The default model (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.

How you actually get the .dmg file

The 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

  1. Clone the repo and run npm ci at the project root.
  2. Run npm run menubar:pack (this runs bundle:ollama then electron-builder --mac). First run downloads the Ollama darwin bundle (~large).
  3. Open the 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)

  1. Set version in package.json to the release you want (e.g. 0.1.1).
  2. Commit, then create and push a matching tag: git tag v0.1.1 && git push origin v0.1.1
  3. Workflow .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.
  4. After the workflow finishes, the site’s Download buttons (via 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.

The Agent Loop Flowchart

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)
        
[Turn 1] User Intent: "Delete all the heavy log files in this folder"
LLM Thought: I need to see what files exist and how big they are first.
↳ Calls Tool: list_directory(".")
[Turn 2] Tool Response: [FILE] server.log (2.1GB), [FILE] error.log (400MB)...
LLM Thought: I see the logs. I'll propose the command to delete them.
↳ Calls Tool: propose_shell_command("rm server.log error.log")
  1. It reads the user's intent and the current working directory.
  2. If it needs to understand the context (like checking if a file exists or seeing what is taking up space), it executes a Read-Only native Node.js tool (like list_directory). This avoids fragile shell-string parsing and OS inconsistencies.
  3. It reads the tool's result directly from the filesystem and loops back to itself.
  4. Once it has enough context, it either replies with a text summary or calls the Action tool (propose_shell_command) to securely queue a command for the user to approve in the UI.

Available Native Tools

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

Capabilities & Limitations

What works 100%

  • Listing files and reading short text previews.
  • Checking disk space availability.
  • Basic file operations (mv, rm, cp) within the selected context folder.
  • Intercepting destructive commands to show UI previews.

What it can't do well (Yet)

  • Long-running processes (like starting dev servers).
  • Interactive shell commands (vim, top).
  • Cross-directory complex operations involving deep sub-folders not explicitly listed.
  • Heavy fuzzy searching (relies on LLM guessing names based on ls output).