Menu

TINY ASSISTANT — LOCAL-FIRST  ·  early

A complete AI assistant in about 1 MB

A local AI assistant that ships as one standalone binary — about 1 MB, no Node, no Python, no dependencies at all. Point it at OpenAI, OpenRouter, or any OpenAI-compatible server and chat from your terminal or Telegram while it remembers facts, edits files, searches the web, and runs scheduled tasks — each capability behind an on/off switch in config.

Continue reading

your terminal or Telegram while it remembers facts, edits files, searches the web, and runs scheduled tasks — each capability behind an on/off switch in config.

# download the asset for your OS/CPU from
# github.com/nullclaw/nllclw/releases/latest
chmod +x nllclw
./nllclw --help

Overview

The useful part, at a glance.

In plain words

What it is
A command-line AI assistant that ships as a single standalone Zig binary — 842 KiB to 1.23 MB depending on platform — that you download, mark executable, and run. Behind that one file are six channels (one-shot answers, an interactive terminal chat, a Telegram bot, a token-guarded WebSocket server, a heartbeat pass over a markdown TODO, and a scheduler daemon), 17 switchable tools, and two layers of plain-JSONL memory on your own disk. It talks to OpenAI, OpenRouter, or any OpenAI-compatible server, including a local Ollama.
Where it fits
The miniature of the family — a complete agent small enough to read in an evening. Start here to learn how an agent actually works, or fork it as your own base; graduate to NullClaw when you need 26 channels.
Why it exists — and when you need it
Why it exists
Most AI assistants stand on a Node or Python runtime and scatter their behavior across a plugin platform; this one fits the entire assistant — provider calls, streaming, the tool loop, memory, all six channels — into 20,903 lines of dependency-free Zig that one person can read end to end. The small size buys unusual auditability, and the code spends it on hardening: base-URL validation, file tools jailed to the working directory, and a tool loop that drops to read-only the moment untrusted web output enters the conversation. Every capability sits behind an on/off switch, so what it cannot do is a config fact, not a hope.
When you need it
Reach for it when you want an AI helper on a machine where installing Node or Python is off the table — a bare VPS, a container, a Raspberry Pi — or when you want scheduled tasks and a Telegram channel from something that idles in under a megabyte. It is also the codebase to pick when you want a complete, tested agent implementation small enough to read in a sitting and fork into your own; skip it if you need Anthropic's API natively, shell execution out of the box, or a plugin ecosystem.

How it works

From zero to running.

nllclw is one file with no runtime underneath it, so the whole flow is: get the binary, answer the wizard once, talk. Everything after that — Telegram, scheduling, the WebSocket server — is the same binary invoked with a different subcommand.

  1. Get the binary

    Download the asset for your OS and CPU from the latest release (twelve platform builds, from 842 KiB on linux-aarch64 to 1.23 MB on Windows) and make it executable. There is nothing else to install — no Node, no Python, no shared libraries. Building from source needs exactly one thing, Zig 0.16.0: zig build --release=small.

    chmod +x nllclw && ./nllclw --help
  2. Run the setup wizard

    init asks for your provider (openai, openrouter, or compatible — any OpenAI-style server, including a local Ollama), your API key, a reply persona, and which capability groups to switch on. It writes ~/.config/nllclw/config.json; any NLLCLW_* environment variable overrides the file, and a .env next to it is the fallback. Undoing everything later is one command: nllclw uninstall.

    ./nllclw init
  3. Talk to it

    Bare on a terminal it opens an interactive chat; given an argument or piped stdin it answers once and exits (prompts capped at 64 KiB). Replies stream token by token by default. Inside any chat channel, slash commands work: /persona switches between four reply styles, /diag runs diagnostics, /settings shows the active config.

    ./nllclw "summarize this" < notes.txt
  4. Verify the setup

    status prints a one-line health check; doctor prints the full diagnostic report across every scope — active provider and model, which capability switches are on, memory paths and message counts, rate limits, and time settings, with secrets redacted. It reports configuration state rather than probing the network, so when the first real question fails, doctor is the fastest way to see which switch is off, which provider is selected, or which path never resolved.

    ./nllclw doctor
  5. Open more channels

    The same assistant, config, and memory serve every channel. nllclw telegram long-polls a bot with a chat allowlist and a per-minute rate limit; nllclw websocket serves a token-authenticated JSON chat protocol on 127.0.0.1:8765/ws; nllclw daemon loops over due schedules and passes over HEARTBEAT.md — a markdown task list the assistant works through on an interval — delivering results locally or to a Telegram chat.

    ./nllclw daemon

Release binaries

One binary, ready to run.

Cross-compiled by nullbuilder for supported platforms — no language runtime or system-wide installer required.

Exact digests come from the repository manifest. If a future release also publishes a checksum file or detached signature, that upstream evidence appears beside the asset.

Capabilities

What nllclw does.

Zero-dependency single binary

One file you download and run — from 842 KiB on linux-aarch64 to 1.23 MB on Windows, twelve platform builds per release. The dependency list in build.zig.zon is empty: HTTP with TLS, the WebSocket server, and JSON parsing all come from Zig's standard library, in a single-threaded executable.

Injection-aware tool loop

Every tool is tagged by whether it changes state and whether its output is trusted. The moment untrusted output — web search results, for example — enters the conversation, all state-changing tools are removed for the rest of the answer, so a hostile web page cannot talk the model into writing files or setting schedules.

Capability-gated tools

17 built-in tools — file list/read/write/edit, memory facts, cron schedules, time, diagnostics, macro management, and web search through five providers (Tavily, Brave, Exa, Firecrawl, or keyless DuckDuckGo) — plus up to 16 macro tools the model defines for itself. Each group sits behind a config switch (set NLLCLW_FILE_WRITE=off and file writes are gone), file tools are jailed to the working directory, and rounds and output bytes per answer are capped. shell_exec is not in the default binary at all: it requires a -Dshell-tool=true build and NLLCLW_SHELL=on.

Any OpenAI-compatible provider

Works with OpenAI, OpenRouter, or any server that speaks the OpenAI chat API — including local ones like Ollama. Replies stream token by token, and the base URL is validated before use: https only, no embedded credentials or query strings, plain http reserved for loopback addresses.

Six channels, one runtime

A one-shot command-line answer, an interactive terminal chat, a Telegram bot, a WebSocket server (loopback-only by default, token required on every connection), a heartbeat pass over a markdown TODO file, and a scheduler daemon — the same assistant, config, and memory everywhere. Chat channels share slash commands like /diag, /settings, and /persona, which switches between four reply styles at runtime.

Local memory and workspace files

Two plain JSONL layers on your disk — the recent transcript plus up to 64 durable facts you ask it to remember — and nothing synced anywhere. Six markdown files in the working directory (IDENTITY.md, SOUL.md, AGENTS.md, MEMORY.md, TOOLS.md, HEARTBEAT.md) plus a skills/ folder of markdown skills are folded into the system prompt, so each project directory shapes its own assistant.

Use it for

Where it earns its place.

A Telegram assistant on a Raspberry Pi

The linux-aarch64 build is 842 KiB and needs no runtime, so a Pi that would choke on a Node install runs it comfortably. Set a bot token and a chat allowlist, start the long-poller, and you have a personal assistant in Telegram with memory, file tools, and web search — rate-limited to 20 messages a minute, with a file lock guaranteeing only one poller per host.

NLLCLW_TELEGRAM_TOKEN=... NLLCLW_TELEGRAM_CHAT_ID=... ./nllclw telegram
Fully local: point it at Ollama, no cloud key

The compatible provider kind accepts any OpenAI-style endpoint, and plain http is permitted for loopback addresses specifically so a local model server works out of the box. Combined with keyless DuckDuckGo search and JSONL memory on your own disk, nothing in the loop has to leave the machine.

NLLCLW_PROVIDER=compatible NLLCLW_BASE_URL=http://127.0.0.1:11434/v1 NLLCLW_MODEL=llama3 ./nllclw
Standing tasks that report to your chat

Ask it in conversation to check something every morning; it calls cron_set, and the schedule lands in schedule.jsonl as one of three kinds — periodic, once, or daily. The daemon claims due entries with a 15-minute lease, runs them, and can deliver output straight to a Telegram chat. You inspect and prune from the CLI, not a web dashboard.

./nllclw daemon
./nllclw schedule list
A per-project assistant defined by markdown

Drop AGENTS.md with project conventions and a skills/ folder of markdown how-tos into a repo, and every nllclw invocation from that directory absorbs them into its system prompt. File tools are jailed to that working directory — absolute paths and .. are rejected — so the assistant can edit the project but cannot wander out of it.

cd ~/code/myproject && ./nllclw "apply our changelog format to CHANGES.md"
What's inside Counted in the source, not the brochure. 38 listed

6 channels

One binary, one config, one memory — a channel is just a different front door into the same runtime.

  • One-shot CLI
  • Interactive REPL
  • Telegram bot
  • WebSocket server
  • Heartbeat
  • Scheduler daemon

17 built-in tools, each behind a config switch

Plus up to 16 macro tools the model defines for itself, and shell_exec as an opt-in 18th that only exists in a -Dshell-tool=true build with NLLCLW_SHELL=on.

  • list_dir
  • read_file
  • write_file
  • edit_file
  • memory_store
  • memory_recall
  • memory_list
  • memory_forget
  • get_time
  • web_search
  • cron_set
  • cron_list
  • cron_delete
  • create_tool
  • list_user_tools
  • delete_user_tool
  • get_diagnostics

5 search providers

An auto selector picks whichever one you have a key for; DuckDuckGo is the keyless fallback.

  • Tavily
  • Brave
  • Exa
  • Firecrawl
  • DuckDuckGo (no key needed)

3 provider kinds, streaming by default

Base URLs are validated before use — https only, no embedded credentials, plain http reserved for loopback.

  • OpenAI
  • OpenRouter
  • Compatible (any OpenAI-style /chat/completions endpoint, including local Ollama)

6 workspace files + a skills folder

Whatever of these exist in the current directory is folded into the system prompt, so each project folder shapes its own assistant.

  • IDENTITY.md
  • SOUL.md
  • AGENTS.md
  • MEMORY.md
  • TOOLS.md
  • HEARTBEAT.md
  • skills/*.md

Quickstart

Up and running.

Full walkthrough in the docs — verified with v2026.6.1.

Run the setup wizard once, then talk to it.

./nllclw init              # setup wizard: provider, style, capabilities
./nllclw "what are you?"   # one-shot question
./nllclw                   # interactive terminal chat
./nllclw status            # quick health line
./nllclw doctor            # full diagnostics

Common questions

Questions, answered.

Does it really have zero dependencies?

Yes, in the verifiable sense: build.zig.zon declares no dependencies, and HTTP with TLS, the WebSocket server, and JSON parsing all come from Zig's standard library. The executable is single-threaded. The trade-off is that everything is hand-rolled, so it supports exactly what it implements — no plugin ecosystem to fill gaps.

Can it run shell commands?

Not in the released binaries. shell_exec is compiled out by default; it only exists if you build from source with -Dshell-tool=true, and even then it stays inert until NLLCLW_SHELL=on. This is a deliberate ceiling: the default binary physically cannot execute shell commands, whatever the model asks for.

What happens if a web page tries to prompt-inject the agent?

Every tool is tagged by whether it mutates state and whether its output is trusted. The moment untrusted output — web search results, say — enters the conversation, all state-changing tools are stripped from the handler set for the rest of that answer. A hostile page can still bias what the model says, but it cannot make it write files, store facts, or set schedules. This behavior has dedicated tests.

Can I use Claude or Gemini with it?

Not directly — the provider layer speaks only the OpenAI chat-completions shape, with three kinds: openai, openrouter, and compatible. To reach Anthropic or Google models you go through OpenRouter or any proxy that exposes an OpenAI-style endpoint. There is no native Anthropic or Gemini client.

Is the WebSocket server safe to expose?

It is designed not to be exposed: it binds 127.0.0.1:8765 and refuses any non-loopback address unless you explicitly set NLLCLW_WS_ALLOW_REMOTE=on. Every connection must present NLLCLW_WS_TOKEN via query or Bearer header, and a per-minute rate limit applies. If you need remote access, tunnel to the loopback port rather than flipping the override.

How stable is it?

Pre-1.0, with a single release so far (v2026.6.1) shipping twelve platform binaries. The CLI and config keys may change between releases, and hard caps are part of the design: 20 transcript messages, 64 memory facts, 16 macro tools, 4 tool rounds per answer, 7-day maximum for periodic schedules. The 'under 1 MB' framing holds for the mainstream 64-bit builds — x86-64 and ARM64 Linux, both macOS builds, ARM64 Android; the Windows, 32-bit ARM, RISC-V, and x86-64 Android builds run 1.01–1.23 MB.

Pre-1.0 with one release (v2026.6.1). Binary sizes are those of that release's published assets; the line count is measured by the project. Config and CLI may change between releases.