Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.45.3] - 2026-07-07

### Fixed
- LLM — OpenAI-compatible requests no longer fail on strict gateways that reject replayed reasoning-only assistant turns as empty messages. ([#246](https://github.com/Fullstop000/ignis/pull/246))

### Deprecated
- TUI — the built-in `ratatui` TUI (`IGNIS_FRONTEND=native`) is deprecated and no longer actively developed; the Ink frontend is the only actively-maintained UI. ([#246](https://github.com/Fullstop000/ignis/pull/246))

## [0.45.2] - 2026-07-03

### Fixed
Expand Down
37 changes: 28 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ This guide contains commands, patterns, and style rules for developers and AI as

## Build and Run Commands

* **Build all workspace crates:** `cargo build`
* **Run interactive TUI (default):** `cargo run`
* **Run interactive TUI (explicit):** `cargo run -- --tui`
* **Run one-shot CLI:** `cargo run -- <prompt>`
* **Build all workspace crates:** `cargo build` (workspace = `ignis` + `ignis-macros`; `ignis-tui/` is a separate Node/Ink project, not a Cargo crate).
* **Run interactive TUI (default):** `cargo run` — no-arg launches the TUI. The UI is the **Ink frontend** (`ignis-tui/`, requires Node ≥18). The built-in `ratatui` TUI is **deprecated** (no longer actively developed) but remains the automatic fallback when Node is missing, or forced with `IGNIS_FRONTEND=native`. (The `--tui` flag was removed in v0.15.0 — no-arg *is* the TUI.)
* **Run one-shot CLI:** `cargo run -- <prompt>` — streams a single turn to stdout and exits.
* **Subcommands:** `ignis mcp`, `ignis upgrade` (alias `update`), `ignis sessions`.
* **Other flags:** `-r, --resume [ID]`, `--afk` (fully unattended), `-v, --version`.
* **Clippy/Lints check:** `cargo clippy --workspace --all-targets -- -D warnings`
* **Rust Formatter:** `cargo fmt --all -- --check`

Expand All @@ -16,6 +17,24 @@ This guide contains commands, patterns, and style rules for developers and AI as
* **Run all unit tests:** `cargo test --workspace`
* **Run specific test file or pattern:** `cargo test <test_name>`

## Architecture

The Rust core lives in `ignis/src/` as directory modules sharing one `Session`-centric loop — entry/UI at the edges, engine in the middle:

* `agent/` — stateless turn-execution engine; runs the tool-dispatch loop and streams `AgentEvent`s (message deltas, tool start/end, turn end).
* `session/` — core conversational model; owns message `history` + persistence and wraps an `Agent`, advancing the conversation via `Session::prompt` / `compact`.
* `llm/` — LLM domain: model catalog, provider-brand declarations, and wire protocols (Anthropic / OpenAI-compatible).
* `tools/` — built-in tool registry (`bash`, `read_file`, `edit_file`, `grep`, `glob`, `agent` sub-agent, `ask_user`, `todo_write`, web, worktree, …) plus the `#[tool]` trait machinery.
* `permissions/` — the tool-call gate; a single 3-state `Mode` (`Off` / `HandsFree` / `FullyUnattended`) + rule set, enforced via a `PermissionChecker` `ToolHooks` impl.
* `hooks/` — external subprocess hooks on `UserPromptSubmit`, `PreToolUse`/`PostToolUse`, and `AssistantMessageRender`; failures degrade to "use original + warn" and never kill a turn.
* `sandbox/` — policy-free process-confinement primitive (Landlock on Linux, Seatbelt on macOS) shared by hook and bash subprocesses.
* `mcp/` — Model Context Protocol client; spawns configured stdio/HTTP MCP servers and exposes their tools as `mcp__<server>__<tool>`.
* `skills/` — user `SKILL.md` instruction sets discovered from disk, advertised to the model, loaded on demand, toggleable at runtime.
* `console/` — the TUI layer: `runner` (event loop + ~30fps frame tick), `app` (state), `render/` (draw), `keys`/`slash`/`composer`/`pickers`, and `frontend/` (the headless `--engine` NDJSON protocol the Ink host drives).
* `cli/` — the clap CLI surface (flags + `mcp`/`upgrade`/`sessions` subcommands) and the Ink-frontend resolver.

Cross-cutting top-level files: `main.rs` (routing — `--engine` headless vs TUI vs one-shot), `config.rs` (TOML config + provider/model resolution), `state.rs` (persisted `state.json`: mode, grants, disabled skills/MCP), `telemetry.rs` (opt-in OpenTelemetry).

---

## Coding Guidelines & Style Rules
Expand All @@ -27,11 +46,11 @@ This guide contains commands, patterns, and style rules for developers and AI as
* Keep changes tightly scoped to the current active goal.

### R2. TUI Design
* The primary UI is a **native terminal TUI** built with `ratatui` + `crossterm`.
* Keep the TUI responsive by processing `AgentEvent` updates at ~30fps.
* Use the established dark color palette (defined in `src/tui.rs`).
* Tool call blocks use color-coded borders: yellow=pending, green=success, red=error.
* Ignis ships as a **single binary** — no external runtime dependencies.
* Two frontends share one Rust core: the **Ink frontend** (`ignis-tui/`, Node/React-Ink — default when Node ≥18 is present, since v0.40.0) and the **built-in `ratatui` TUI** (`crossterm` backend). The ratatui TUI is **deprecated** and no longer actively developed — it remains as a fallback when Node is unavailable, forced with `IGNIS_FRONTEND=native`. The Ink host owns the terminal and spawns the Rust binary as a headless `--engine` over an NDJSON stdin/stdout protocol.
* The built-in TUI renders at a ~30fps frame tick (`FRAME = 33ms` in `console/runner.rs`); `AgentEvent`s stream in between frames and are coalesced into the next draw.
* Use the established dark color palette (Catppuccin Mocha, defined in `src/console/colors.rs`).
* Tool call blocks encode status via a color-coded **bullet** (`●`): yellow=pending, green=success, red=error (`console/render/tool_block.rs`).
* The Rust core ships as a **single binary**; the Ink frontend additionally requires Node ≥18 (the deprecated `ratatui` TUI is the only no-runtime-deps path).

### R3. Quality & Warning Gate
* Maintain **zero compiler warnings and clippy errors** in the Rust crates.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ Drops the binary in `~/.ignis/bin` and the Ink frontend in `~/.ignis/ignis-tui`.
Already installed? Update in place with `ignis upgrade`.

`ignis` runs the [Ink frontend](ignis-tui/README.md) by default when **Node ≥18**
is on your PATH, and falls back to the built-in `ratatui` TUI otherwise. Force the
built-in any time with `IGNIS_FRONTEND=native`.
is on your PATH, and falls back to the built-in `ratatui` TUI otherwise. The
ratatui TUI is **deprecated** (no longer actively developed); `IGNIS_FRONTEND=native`
forces it.

<details>
<summary>Other ways to install</summary>
Expand All @@ -44,7 +45,7 @@ ignis upgrade --version v0.14.1 # pin to a specific tag
git clone https://github.com/Fullstop000/ignis.git
cd ignis && cargo build --release # → target/release/ignis

# Optional: the experimental Ink frontend (Node required). Built from a source
# Optional: the Ink frontend (Node required). Built from a source
# checkout, `ignis` launches it by default; install its deps once first.
( cd ignis-tui && npm install )
```
Expand Down Expand Up @@ -81,9 +82,9 @@ See [Configure](#configure) for more providers and per-model options.

## Features

- **TUI + CLI** — a terminal TUI and a one-shot CLI from the same binary. The
default UI is the [Ink frontend](ignis-tui/README.md) when Node is present, with
the built-in `ratatui` TUI as the always-available fallback (`IGNIS_FRONTEND=native`).
- **TUI + CLI** — a terminal TUI and a one-shot CLI from the same binary. The UI
is the [Ink frontend](ignis-tui/README.md) when Node is present, with the built-in
`ratatui` TUI as a deprecated fallback for Node-less environments (`IGNIS_FRONTEND=native`).
- **Bring your own model** — OpenAI, Anthropic, DeepSeek, Kimi, MiniMax,
Moonshot, Ollama, and any OpenAI-compatible endpoint (the `custom` provider).
Providers are built in — drop in an API key and go. Switch model and reasoning
Expand All @@ -102,8 +103,8 @@ See [Configure](#configure) for more providers and per-model options.
gate, with a built-in safety floor and user-declarable allow/ask/deny rules.
- **Sessions** — project-scoped history with `--resume`, auto-resume, and
context compaction; export per-session stats with `ignis sessions export`.
- **Single binary** — the core agent is one self-updating binary with no runtime
deps; the optional Ink frontend is the only piece that needs Node.
- **Single binary** — the core agent is one self-updating binary; the Ink frontend
needs Node ≥18 (the deprecated `ratatui` TUI is the no-Node fallback).

## Configure

Expand Down Expand Up @@ -146,14 +147,14 @@ the active selection at runtime, saving it to `~/.ignis/state.json` — your
| `ignis upgrade` | Update to the latest release |
| `ignis --help` | Full flag and subcommand list |

In the TUI: `Enter` sends, `↑/↓` walk history, `Ctrl+D` exits. Output renders
In the TUI: `Enter` sends, `↑/↓` walk history, `Ctrl+D` twice exits. Output renders
inline in the normal buffer, so scroll with your terminal/tmux as usual. Type
`/` for slash-command suggestions — see
[`docs/usage/commands.md`](docs/usage/commands.md) for the full reference.

## Docs

Deep references live in [`docs/`](docs/README.md) — commands, permissions,
Deep references live in [`docs/`](docs/README.md) — commands, hooks, permissions,
skills, MCP servers, telemetry.

## Development
Expand Down
10 changes: 7 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ feature list — these pages go deeper.
How to drive ignis day to day.

- [Commands](usage/commands.md) — every built-in TUI slash command (`/sessions`,
`/clear`, `/compact`, `/copy`, `/model`, `/skills`, `/mcp`, `/afk`,
`/telemetry`) plus the `/<skill-name>` force-load form, with a global
keybindings table.
`/clear`, `/compact`, `/copy`, `/connect`, `/model`, `/skills`, `/mcp`, `/afk`,
`/hooks`, `/settings`, `/telemetry`) plus the `/<skill-name>` force-load form,
with a global keybindings table.
- [Hooks](usage/hooks.md) — external subprocess hooks on the
`UserPromptSubmit`, `PreToolUse`/`PostToolUse`, and `AssistantMessageRender`
lifecycle events: envelope, exit codes, the `~/.ignis/hooks.json` declaration,
and the per-hook filesystem sandbox.

## Configure

Expand Down
9 changes: 6 additions & 3 deletions docs/configure/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,12 @@ predictable:
(or `bearer_token_env_var = "…"` in TOML). For stdio, pass credentials via
`-e KEY=VALUE` or the server's own config file.
- **MCP resources and prompts** — only `tools/*` surfaces are wired.
- **Per-tool permission rules** — the existing
[permissions](permissions.md) system gates the wrappers as a group; per-tool
allow/deny is not yet supported.
- **Per-server glob permission rules** — the existing
[permissions](permissions.md) system gates each MCP tool by its full
`mcp__<server>__<tool>` name, so you can `allow`/`ask`/`deny` an individual
tool (e.g. `deny = ["mcp__github__create_issue"]`). What's *not* supported is
globbing across a server's tools (`mcp__github__*`) — list each tool by name,
or gate the bare name.
- **`${VAR}` interpolation in `headers`** — secrets belong in
`bearer_token_env_var`; literal headers stay literal.
- **Mid-session reconnect** on a hard transport drop. rmcp handles
Expand Down
31 changes: 28 additions & 3 deletions docs/configure/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,34 @@ unattended they hard `Deny`.

The floor is intentionally small, covers catastrophic-and-easily-
recognized cases, and doesn't try to reason about every destructive
command. Sandbox-level enforcement (Linux Landlock) is on the roadmap.
command.

## Bash sandbox

For auto-approved `bash` in the unattended modes (hands-free / AFK),
ignis can additionally confine each spawned command with a filesystem
sandbox — a defense-in-depth layer *below* the permission gate. It is
**opt-in and off by default** (so credentialed commands like `git push`
work out of the box); turn it on in `/settings` → *Sandbox auto-run bash*.
The choice persists in `~/.ignis/state.json`.

When on, the sandbox confines **writes** to the project directory, the
temp dirs, `/dev/null`, and any configured `sandbox_write_paths`. On
**Linux** (Landlock, ABI V2) it also narrows **reads** to system roots,
the project, temp, and the Rust toolchain caches (`~/.cargo`,
`~/.rustup`), so `$HOME` credential dirs (`~/.ssh`, `~/.aws`, `~/.gnupg`,
`~/.ignis`) stay unreadable — extend reads with `sandbox_read_paths`. On
**macOS** (Seatbelt) the read narrowing isn't implemented; writes are
still confined.

```toml
[permissions]
sandbox_write_paths = ["~/projects/shared", "/var/cache/myapp"]
sandbox_read_paths = ["~/.npm", "/opt/sdk"]
```

The sandbox only engages for auto-run (unattended) `bash` — under `Off`,
or with the toggle off, bash runs unsandboxed.

## Roadmap

Expand All @@ -176,7 +203,5 @@ Not yet shipped:
- `plan` mode — read-only exploration with no edits.
- **Multi-scope rule layering** — managed > project > user precedence for
the rule grammar (today there's one `config.toml`).
- **OS-level sandboxing** — Linux Landlock filesystem restrictions for
bash calls, as a defense-in-depth layer below the permission gate.

Anything not listed above is not on the near-term roadmap.
4 changes: 0 additions & 4 deletions docs/configure/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,6 @@ defense in depth.

## Known limitations (v1)

- **Anthropic provider does not emit token usage** at all today (separate
pre-existing gap in the provider's stream parser). Anthropic users will see
spans but zero token-usage metric points. Follow-up issue:
add `message_delta` event parsing to `provider/anthropic.rs`.
- **No distributed tracing** into bash/MCP subprocesses (`traceparent`
propagation). Deferred to v2; rare need for the current use cases.
- **Cost is not computed inside ignis.** Compute it backend-side from
Expand Down
47 changes: 44 additions & 3 deletions docs/usage/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ no native dependency.

---

### `/connect`

Connect a provider and pick a default model. The picker walks through choosing
a provider brand, entering credentials, and selecting a model; the choices are
saved to your ignis config (`~/.ignis/config.toml`). It's also the landing
screen when no provider is configured yet.

```
/connect
```

---

### `/model`

Open the model picker. Lists every model declared under each configured
Expand Down Expand Up @@ -127,8 +140,8 @@ confirmation picker before flipping state.

### `/telemetry`

Print the current OpenTelemetry exporter status (endpoint, headers redacted,
sample run-time counters) as an assistant notice. Read-only.
Show the current OpenTelemetry exporter status (endpoint, headers redacted,
sample run-time counters) as an assistant notice, and toggle export on or off.

```
/telemetry
Expand All @@ -138,6 +151,34 @@ See [configure/telemetry.md](../configure/telemetry.md) for setup.

---

### `/hooks`

List the hook chains the running session actually uses (`/hooks` or
`/hooks list`), or re-read `~/.ignis/hooks.json` after editing it
(`/hooks reload`). One block per event, each entry showing the program path,
argv tail, and per-hook timeout. See [hooks](hooks.md) for the full protocol.

```
/hooks
/hooks reload
```

---

### `/settings`

Toggle live session settings from a panel — the bash sandbox (confine
unattended `bash` to the project + temp, away from `$HOME` secrets; off by
default), auto-compaction, stripping reasoning from history, and which
statusline segments show (model / cwd / git branch / turns / tokens). Choices
persist in `~/.ignis/state.json`.

```
/settings
```

---

### `/<skill-name>`

For any enabled skill, typing its name as a slash command force-loads the
Expand Down Expand Up @@ -170,4 +211,4 @@ keys apply while the TUI input is active:
| `Ctrl+W` | Delete previous word |
| `Ctrl+S` | Steer the running turn (queue an instruction mid-stream) |
| `Ctrl+C` | Cancel the running turn / clear the input |
| `Ctrl+D` | Exit ignis |
| `Ctrl+D` (twice) | Exit ignis — the first press prompts "Press Ctrl-D again to exit" |
Loading
Loading