A terminal for your toolkit. Not an environment.
  • Rust 91.8%
  • Python 6.7%
  • Nix 0.7%
  • Shell 0.3%
  • WGSL 0.2%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
natsukium 351d451ad6
All checks were successful
fuzz / cargo fuzz nightly long-run (push) Has been skipped
pr / frontend smoke (x86_64-linux) (push) Successful in 55s
windows / detect relevant changes (push) Successful in 6s
windows / windows docs-only no-op (push) Has been skipped
pr / publish felis (x86_64-linux) (push) Successful in 12s
windows / frontend smoke (Windows) (push) Successful in 1m38s
darwin / detect relevant changes (push) Successful in 4s
darwin / darwin docs-only no-op (push) Has been skipped
darwin / build felis (aarch64-darwin) (push) Successful in 51s
fuzz / cargo fuzz smoke (per target) (push) Successful in 1m6s
pr / nix flake check (push) Successful in 8s
pr / cargo build / clippy / test / deny (push) Successful in 1m37s
pr / wire schema is compatible with the base (push) Successful in 13s
windows / cargo clippy (Windows cross) (push) Successful in 12s
windows / cargo nextest (Windows) (push) Successful in 4m17s
windows / package felis (x86_64-pc-windows-msvc) (push) Successful in 1m45s
treewide: drop operation ids from the v0.1 wire contract (#298)
Implements the decision on #296: [remove operation ids from the v0.1 contract](#296 (comment)) (option 1).

## Why

No message ever read the daemon's landing records back, so the minted token named a fan-out nothing could ask about: the CLI and the bridge report `queued` and exit, and the shipped agent skill lists the switch verbs under the surfaces it does not cover. Freezing `<incarnation:016x>-<sequence>` as contract before the consumer that reads it exists is the worst of both options, since a field whose meaning arrives later cannot change shape when it does. Adding the id back is an additive minor whenever a consumer appears.

## Scope of the removal

- **Proto**: `OperationId`, `OperationEcho`, `OpsSwitched.operation_id`, `PushReattach.operation`, `PushRetargetHost.operation`, `SessionAttach.operation`. Tags are left free, not reserved; the break is acknowledged with a `base:` entry in `crates/felis-protocol/proto/BREAKING.md`, and the codegen is regenerated.
- **Protocol crate**: `MINOR_OPERATION_IDS` and its re-export, the four gated-field entries and their tests, the domain types, and the converts. The minor-8 ledger row keeps `Spawn` / `Created` and the `accepted` → `queued` rename.
- **Daemon**: `src/operations.rs` and the `Operations` handle on the pool, the mint at the `OpsSwitch` handler, the `land` call at the attach path, and the per-target stamping in the session task. A switch push now answers with a count rather than the list of attachment ids nothing reads.
- **Client**: the echo threading through `felis-client-core` (`connector.rs`, `dial.rs`) and `felis-client`; `attach_landing` folds back into `attach`.
- **CLI / bridge**: `operation_id` leaves the `switch`, `ssh` and `window retarget` objects and the bridge results, and `crates/felis-cli/schemas/felis-cli-v1.schema.json` is regenerated. The bridge goldens held no successful `sessions.switch` result, so a success-path test now pins `{from, to, queued}`.
- **Kept**: `queued` as the sole completion claim, `not_queued` as its error kind.
- **Docs**: `docs/reference/ipc.md` (Switch/Switched, the deleted "Operation ids" section, the minor-8 row), `docs/reference/cli.md`, the rewritten "What a switch reply can report" in `docs/explanation/architecture/ipc.md`, and a `CHANGELOG.md` entry for the machine-output and bridge-result key.

The skew outcome is stated in `BREAKING.md` rather than handled: the removed fields decode as unknown and therefore absent in both directions, so the switch path works between a daemon and a client on either side of the change.

## Verification

- `just check` green (fmt, clippy, tests, prose-check, skill-check, docs-links, deny, proto-compat, release-check). The pre-commit `buf breaking` hook has no acknowledgment path, so the commits skip that one hook; the CI gate `just proto-compat` reads `BREAKING.md` and passes.
- `grep -rn 'OperationId\|OperationEcho\|MINOR_OPERATION_IDS\|operation_id' crates/ docs/ skills/` returns only `BREAKING.md`.
- Two `openai-codex/gpt-5.6-sol` reviews: round 1 PASS with two non-blocking findings (vestigial `Vec<u64>` in `PushOutcome`; the nonzero-`queued`-at-minor-0 invariant left unpinned), both fixed in 9403ccef; round 2 PASS with no findings.

Closes #296

Reviewed-on: #298
2026-09-19 13:54:59 +09:00
.agents/skills ci: run repository gates through Python (#295) 2026-09-18 15:54:46 +09:00
.cargo Speed up cargo-mutants runs (#274) 2026-09-13 14:57:25 +09:00
.claude claude: remove unused config (#265) 2026-09-12 18:31:57 +09:00
.config refactor: prune redundant and conversational comments across CI, Nix, and configs 2026-09-04 10:17:10 +09:00
.forgejo ci: run repository gates through Python (#295) 2026-09-18 15:54:46 +09:00
crates treewide: drop operation ids from the v0.1 wire contract (#298) 2026-09-19 13:54:59 +09:00
dev tests: replace workspace guards with repository linters (#294) 2026-09-18 16:08:51 +09:00
docs treewide: drop operation ids from the v0.1 wire contract (#298) 2026-09-19 13:54:59 +09:00
fuzz deps: Lock file maintenance 2026-09-14 00:33:42 +09:00
nix nix: separate repository tooling from packaging (#268) 2026-09-12 22:00:38 +09:00
share/terminfo docs: sweep British spellings back out 2026-08-31 00:40:50 +09:00
skills/felis transport: move the daemon endpoint to /tmp/felis.<uid> (#283) 2026-09-15 18:52:39 +09:00
tests tests: replace workspace guards with repository linters (#294) 2026-09-18 16:08:51 +09:00
tools ci: run repository gates through Python (#295) 2026-09-18 15:54:46 +09:00
.gitattributes refactor: prune redundant and conversational comments across CI, Nix, and configs 2026-09-04 10:17:10 +09:00
.gitignore refactor: prune redundant and conversational comments across CI, Nix, and configs 2026-09-04 10:17:10 +09:00
AGENTS.md tests: replace workspace guards with repository linters (#294) 2026-09-18 16:08:51 +09:00
Cargo.lock deps: Update Rust crate clap_complete to v4.6.11 (#297) 2026-09-19 03:33:23 +09:00
Cargo.toml Speed up cargo-mutants runs (#274) 2026-09-13 14:57:25 +09:00
CHANGELOG.md treewide: drop operation ids from the v0.1 wire contract (#298) 2026-09-19 13:54:59 +09:00
CLAUDE.md skills: restore Claude skill discovery symlink 2026-09-06 01:23:37 +09:00
clippy.toml config: drop settings that restate a tool default (#264) 2026-09-12 17:55:07 +09:00
CONTRIBUTING.md docs: give every fact one owning page and cut what restates it (#289) 2026-09-18 11:41:53 +09:00
deny.toml tests: replace workspace guards with repository linters (#294) 2026-09-18 16:08:51 +09:00
flake.lock deps: Lock file maintenance (#279) 2026-09-14 14:21:40 +09:00
flake.nix nix: isolate dev inputs in partitions.dev so consumers only fetch what they need (#162) 2026-09-06 21:20:03 +09:00
justfile tests: replace workspace guards with repository linters (#294) 2026-09-18 16:08:51 +09:00
LICENSE Lock in license, filesystem layout, OSC 66, and Sixel as permanent reject 2026-05-10 02:19:06 +09:00
README.md docs: give every fact one owning page and cut what restates it (#289) 2026-09-18 11:41:53 +09:00

felis

A terminal for your toolkit. Not an environment.

felis is a GPU-accelerated terminal that keeps your sessions alive and renders modern terminal protocols, while leaving layout to your window manager and automation to external tools.

What felis owns

  • Sessions, beyond the window. Close the window without killing the shell. Reattach locally or over SSH with the grid and scrollback still in the daemon. Sessions live in memory, not across daemon restarts or reboots.
  • Modern terminal rendering. Kitty graphics and text sizing, true color, ligatures, font fallback, and native IME, without an in-terminal multiplexer between the application and the renderer.
  • An interface for your tools. Typed CLI commands and a public, versioned IPC let scripts and programs drive sessions, send input, and read output.

The rest stays yours

Your window manager handles layout. Your shell and tools handle workflow. felis provides the interfaces for those tools to work with it, without moving their logic inside the terminal. There are no in-terminal tabs or splits, and no embedded scripting language.

Small utilities belong alongside the terminal: a session picker, a notification consumer, an editor integration. They can use the same public interfaces as your own scripts. The point is to let you choose and combine tools, not require an entire toolset to use a terminal.

Read the vision for where felis draws the line, and the design values for why.

Quick start

With Nix and flakes enabled:

nix run git+https://git.natsukium.com/natsukium/felis

A window opens on a fresh session, and the shell inside it keeps running once you close the window. The install guide covers the profile, home-manager, Stylix and from-source paths; your first session walks the close-and-reattach loop.

Working with your tools

felis --host user@remote puts a local window on a remote daemon that keeps its sessions across disconnects (attach over SSH). Session verbs speak JSON (--format json or jsonl) and felis bridge keeps a connection open for a longer-lived integration (driving a session without a window, CLI reference, IPC protocol). One TOML file controls fonts, colors, keybindings, and window appearance (configuration, keybindings).

Status and compatibility

felis is at 0.1.0, an early release distributed as a Nix flake. The CLI and IPC are versioned, but expect edges.

x86_64-linux has a passed runtime release gate and a published build. x86_64-pc-windows-msvc passes the runtime gate but is available as a CI artifact rather than a distribution. aarch64-linux and aarch64-darwin build and run tests in CI; neither has a claimed runtime release gate. See the platform matrix for details and the protocol support matrix for terminal compatibility.

Open work is tracked in Forgejo issues.

Documentation and development

The manual is published at https://felis-docs.pages.dev; its source and full index live in docs/.

To understand the design, read the vision, values, and testable principles, in that order. For architecture and comparisons with other terminals, follow the explanation index.

felis is written in Rust with wgpu and winit. The Nix flake owns the development toolchain:

nix develop
cargo build

See CONTRIBUTING.md for the development workflow and verification gates, or the install guide to build and run from source.

License

Apache-2.0. See LICENSE.

One file is excepted: tools/bench/doom-fire-bench.patch is GPL-3.0-or-later, since a patch carries the upstream lines it modifies and DOOM-fire-zig is GPLv3. It is applied to that project's source at build time by the benchmark dev shell and reaches no felis binary.