Inzen CLI Bot
Building a Windows Executable & Installer
To create a standalone Windows EXE and one-click installer:
- Build the EXE:
- Install PyInstaller:
pip install pyinstaller - Run the build script:
build_exe.bat - The EXE will appear in the
dist/folder.
- Create an Installer (optional):
- Use Inno Setup or NSIS to package the EXE and any required files.
- See
installer.iss.examplefor a sample Inno Setup script (if present).
Note: The EXE includes Python and all dependencies—no Python install required for users.
Building Linux .deb and .rpm Packages
nfpm works cross-platform, so you can build Linux packages from Windows, macOS, or Linux without a VM.
1. Install nfpm (one-time)
Windows (PowerShell) — choose one:
# Scoop
scoop install nfpm
# Chocolatey
choco install nfpm
# winget
winget install GoReleaser.nfpm
# Direct download (no package manager required)
$tag = "v2.43.2"
$url = "https://github.com/goreleaser/nfpm/releases/download/$tag/nfpm_Windows_x86_64.zip"
Invoke-WebRequest $url -OutFile "$env:TEMP\nfpm.zip"
Expand-Archive "$env:TEMP\nfpm.zip" -DestinationPath "$env:LOCALAPPDATA\nfpm" -Force
$env:PATH += ";$env:LOCALAPPDATA\nfpm" # add permanently via System Properties > Environment Variables
Linux / macOS:
# Homebrew
brew install nfpm
# Direct binary
curl -sfL https://install.goreleaser.com/github.com/goreleaser/nfpm.sh | sh
2. Build the packages
Windows (PowerShell):
.\build_linux_packages.ps1
Linux / macOS:
./build_linux_packages.sh
This produces:
dist/linux/inzen-bot_<version>_<arch>.debdist/linux/inzen-bot_<version>_<arch>.rpm
The packages install the standalone binary under /opt/inzen-cli-bot/, expose both bot and inzen_cli_bot on the system PATH, include the project docs under /usr/share/doc/inzen-cli-bot/, and print a post-install reminder to run bot --setup.
On Git tags matching v*, GitHub Actions builds and attaches all release installers to the matching GitHub Release automatically:
- Windows standalone EXE
- Windows installer EXE
- Linux
.deb - Linux
.rpm
No browser. No switching apps. Just ask and keep working.
Contents
Security & Secrets
- Never commit real API keys or secrets. Only
.env.exampleshould be public. - The
.gitignoreexcludes.env,.venv/, and build artifacts by default.
- Install
- Setup
- Usage
- Interactive Mode
- Named Sessions
- Token Usage
- Providers
- Configuration
- Security
- Requirements
- Contributing
- Releasing to PyPI
Features
Code Style
- Use ruff for linting:
uv run ruff check . - Use black for formatting:
uv run black . - Add type hints to all functions.
- Named sessions — keep separate threads for different projects, all stored locally
- Interactive REPL —
bot --chatfor a persistent conversation without re-typing flags - Token & cost tracking — per-message footer and a
bot --usagesummary table across all providers - Session export — dump any session to markdown or plain text
- Secure by default — local data files are user-only (600/700), session names are validated against path traversal
Install
pip install inzen-bot
Or with uv:
uv add inzen-bot
Setup
1. Run interactive setup (recommended)
bot --setup
This wizard will:
- Ask which providers you want to use
- Ask for model/base URL where needed
- Store API keys securely in your OS keyring (when available)
- Save provider settings to
~/.bot/config.json
2. Or set API keys manually
Inzen CLI Bot reads your API keys from environment variables.
Linux / macOS — add to your ~/.bashrc or ~/.zshrc:
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..." # optional
Then reload:
source ~/.bashrc
Windows (PowerShell) — set permanently:
[System.Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY", "sk-ant-...", "User")
Restart your terminal after running this.
For Ollama and llama.cpp no API key is needed — just make sure the local server is running before use.
2. Verify it works
bot hello
You should see a streamed response from Claude with a token usage footer. If you get an error, check your API key:
# Linux / macOS
echo $ANTHROPIC_API_KEY
# Windows PowerShell
echo $env:ANTHROPIC_API_KEY
Usage
Ask anything — history is automatic so you can follow up naturally:
bot how do I format a drive in Ubuntu?
bot step 2?
bot step 3?
Common flags:
# Use a specific provider for one query
bot --provider openai explain RAID levels
# Run setup wizard again
bot --setup
# Update from latest GitHub release
bot --update
# Set local-provider endpoints without editing config.json
bot --set-ollama-url http://localhost:11434
bot --set-llamacpp-url http://localhost:8080
# See API key status (keyring/env)
bot --credentials-list
# Update or remove a stored API key
bot --credentials-update anthropic
bot --credentials-remove anthropic
# Switch default provider permanently
bot --set-provider ollama
# Switch model
bot --set-model claude-opus-4-5
# Toggle insecure remote HTTP provider endpoints in config
bot --allow-insecure-http
bot --no-allow-insecure-http
# View conversation history
bot --history
# Purge data older than the configured retention window
bot --purge
# Clear history
bot --clear
# List all configured providers
bot --providers
# Override the system prompt for one query
bot --system "You are a Python expert" explain decorators
# Redact likely secrets in history/export output
bot --history --redact-secrets
# Export outside safe directories (disabled by default for safety)
bot --export myproject --output /tmp/myproject.md --unsafe-output
bot --update checks the latest GitHub release and updates in place for Python installs, Linux .deb/.rpm package installs, and Windows installer-based installs.
Interactive Mode
Use --chat for a persistent REPL — no need to retype flags between messages.
# Start a persistent chat session
bot --chat
# Start with an opening message, then continue the conversation
bot --chat explain how systemd services work
# Use a named session in interactive mode
bot --session myproject --chat
Inside the chat session the following slash commands are available:
| Command | Description |
|---|---|
/help |
Show available commands |
/history |
Print the conversation so far |
/clear |
Clear conversation history for this session |
/exit |
Exit chat mode (also: /quit, q) |
Named Sessions
Named sessions let you maintain separate, persistent conversation threads — useful for keeping different projects or topics isolated.
# Start or continue a named session
bot --session myproject what is a venv
# Pick up where you left off
bot --session myproject how do I activate it
# List all saved sessions
bot --sessions
# Export a session to markdown
bot --export myproject
# Export to a file
bot --export myproject --output myproject.md
# Delete a session
bot --clear-session myproject
Sessions are stored as JSON files in ~/.bot/sessions/ and are independent of provider — you can switch providers mid-session.
Token Usage
Every response includes a per-message token and cost footer. Run bot --usage to see cumulative totals across all providers:
# View cumulative usage across all providers
bot --usage
Providers
| Provider | Requires | Notes |
|---|---|---|
anthropic |
ANTHROPIC_API_KEY |
Default. Claude models. |
openai |
OPENAI_API_KEY |
GPT models. Also works for Groq, Together, etc. |
ollama |
Ollama running | Local inference. Free to run. |
llamacpp |
llama.cpp running | Local inference via OpenAI-compatible server. |
Ollama quickstart
# Install from https://ollama.ai, then:
ollama pull llama3
ollama serve
bot --set-provider ollama
bot hello from the terminal
llama.cpp quickstart
# Build llama.cpp from https://github.com/ggerganov/llama.cpp, then run the server:
./server -m your-model.gguf --port 8080
bot --set-provider llamacpp
bot what is quantisation
Using Groq or other OpenAI-compatible APIs
Point the OpenAI provider at any compatible endpoint via ~/.bot/config.json:
{
"providers": {
"groq": {
"model": "llama-3.3-70b-versatile",
"api_key_env": "GROQ_API_KEY",
"base_url": "https://api.groq.com/openai/v1"
}
}
}
Configuration
Config and history live in ~/.bot/, with user-only file permissions on Linux and macOS:
~/.bot/ (chmod 700)
├── config.json (chmod 600) — provider settings and defaults
├── history_anthropic.json (chmod 600) — conversation history per provider
├── history_openai.json (chmod 600)
├── history_ollama.json (chmod 600)
├── usage_anthropic.json (chmod 600) — cumulative token/cost data
└── sessions/ (chmod 700)
└── myproject.json (chmod 600)
History is capped at 50 message pairs per provider. You can edit ~/.bot/config.json directly to customise models, add providers, or set custom base URLs.
Security-related defaults in config.json:
{
"security": {
"safe_output": true,
"redact_secrets": false,
"warn_dangerous_commands": true,
"allowed_export_dirs": ["."],
"allowed_hosts": [],
"allow_insecure_http": false,
"request_timeout_seconds": 30,
"max_retries": 2,
"retention_days": 30
}
}
Security
- File permissions —
~/.bot/and all data files are created with user-only permissions (700/600) on Linux and macOS so other users on the same machine cannot read your history, API keys, or session data. - Session name validation — session names are validated against a strict allowlist (letters, numbers,
.,_,-) to prevent path traversal attacks. Names like../etc/passwdare rejected at the CLI boundary. - Safe exports —
--outputwrites are restricted to allowed directories by default; use--unsafe-outputto bypass for one command. - Secrets redaction — use
--redact-secretsto mask common key/token patterns in--historyand--exportoutput. - Network guardrails — provider
base_urlvalues are validated (https by default for non-local hosts), with configurable host allowlists, timeouts, and retry limits. - Outbound warning guardrail — bot warns before printing responses that appear to contain dangerous shell command suggestions.
- Retention controls — use
--purgeto delete history, usage, and session files older than the configured retention window. - Security log — export, clear, purge, and config changes are recorded in a local redacted JSONL audit log at
~/.bot/security.log. - Security policy — see SECURITY.md for reporting guidance and supported-version details.
- Threat model — see THREAT_MODEL.md for threat boundaries, mitigations, and residual risks.
- API keys — keys are never written to disk; they are read only from environment variables at runtime.
Requirements
- Python 3.12+
anthropic— Anthropic/Claude provideropenai— OpenAI and llama.cpp providersrich— terminal renderingclick— CLI framework
Cross-platform
Works on Linux, macOS, and Windows. Config and history paths resolve correctly on all platforms via Path.home(). File permission hardening applies on Unix only; Windows uses its own ACL-based access controls.
Built by
Inzen — AI consulting and LLM application development.
Contributing
git clone https://github.com/benwalkerai/bot
cd bot
uv sync --group dev
uv run pytest # run tests
uv run ruff check . # lint
CI runs automatically on every push and pull request via GitHub Actions — tests and ruff must pass before merging.
Releasing
Current automated release flow
From v0.3.6 onward, releases are published through GitHub Actions and include:
- PyPI package publish (
inzen-bot) - Windows installer EXE
- Linux
.deb - Linux
.rpm
The release pipeline validates that the Git tag version matches pyproject.toml before assets are published.
Recommended: one-click release (workflow dispatch)
Use the Cut Release workflow in GitHub Actions:
- Open Actions → Cut Release.
- Click Run workflow on
main. - Choose
release_type(patch,minor, ormajor). - Optional: set
version(for explicit version, e.g.0.3.7).
What this workflow does automatically:
- Bumps version in
pyproject.toml(and bump-my-version metadata). - Runs release checks:
uv run pytest -vuv run ruff check .uv lock --checkuv run pip-audit
- Regenerates
CHANGELOG.mdwithgit-cliff. - Commits release files to
main. - Creates and pushes tag
vX.Y.Z. - Triggers:
publish.ymlto publish on PyPIrelease-assets.ymlto publish Windows/Linux assets
Manual fallback (if needed)
# 1) Bump version without auto-commit/tag
uv run bump-my-version bump patch --no-commit --no-tag
# 2) Regenerate changelog
uv run git-cliff --output CHANGELOG.md
# 3) Run release checks
uv run pytest -v
uv run ruff check .
uv lock --check
uv run pip-audit
# 4) Commit and tag
git add pyproject.toml uv.lock CHANGELOG.md
git commit -m "chore: bump version to <version>"
git tag -a v<version> -m "v<version>"
# 5) Push
git push origin main
git push origin v<version>
Required repository secret for publish:
PYPI_API_TOKEN
License
GPL-3.0
Metadata
Release files for inzen-bot 0.3.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| inzen_bot-0.3.7.tar.gz | 51.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| inzen_bot-0.3.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.9 kB
Release files / inzen_bot-0.3.7.tar.gz
| Download URL | inzen_bot-0.3.7.tar.gz |
|---|---|
| Size | 51.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca23dd6be5b07840c8573c0fa65a2a1d0b262bafb9f36137e06b59d5fccb84ff
|
|
BLAKE2b-256 checksum How to use checksums |
aafc5facc0ff5d291270e719b322ad73480570bf0ca62dfc7c6a613f350b417f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / inzen_bot-0.3.7-py3-none-any.whl
| Download URL | inzen_bot-0.3.7-py3-none-any.whl |
|---|---|
| Size | 41.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f6a57d62305db0888c614f30edb97afb3eef66dc88b059ce87342b83d4abce85
|
|
BLAKE2b-256 checksum How to use checksums |
df17ad95624e1d900d249a993bd5056285e180ad3874f35577b57f499371b18a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|