closes#137
- right arrow consumed the escape sequence without forwarding it, so the
cursor couldn't move back right after moving left (60149a2)
- p10k's instant prompt redirects stdout to a temp file that exec iris
inherits, sending the overlay there instead of the terminal and looking
like a dead keyboard. Reopens /dev/tty when stdout isn't a terminal
(7ebbb0c)
- zsh/bash autostart lacked the interactivity check fish already had, so
agentic tools sourcing your rc file spawned a rogue iris that seized
/dev/tty (5fe58be)
closes#91
3 mode config atuin history
```toml
[core]
# 0 = off, 1 = atuin history only, 2 = atuin history + default history
atuin-history = 0
```
Default is Mode 0 (turn off), who uses atuin history has to config it
manually
Mode 1 = atuin only (as you can see only 11 commands):
<img width="1285" height="466" alt="image"
src="https://github.com/user-attachments/assets/ca520ec7-fb45-4403-8087-a4951ba8dba0"
/>
Mode 2 = combine shell history + atuin history (prefer atuin):
<img width="1261" height="469" alt="image"
src="https://github.com/user-attachments/assets/1558ad3e-38f3-4d6e-bc8e-cfc00ea9cb4b"
/>
Mode 0 = turn off atuin
---
Custom atuin db path if anyone's using their own path (leave empty to
use default from atuin)
```toml
atuin-db-path = ""
```
---
I also handled the search menu overlay of Atuin, IRIS won't show in this
mode, but you have to change the keybind between 2 tool cuz they're
using same keybind Ctrl + R, but I think using IRIS make it unnecessary
to use Atuin search mode (82b180c9610f75d41008a54dfde474c7f2cce015)
<img width="70%" alt="image"
src="https://github.com/user-attachments/assets/e0c62050-7e6e-4e37-a1cc-2bfb63e1e984"
/>
<i><b>Before (Menu layout completely broken)</b></i>
<img width="50%" alt="image"
src="https://github.com/user-attachments/assets/1e6d8de9-cde8-473d-8a42-4748c480c812"
/>
<i ><b>After handling (not shows anymore)</b></i>
closes#130
- CHANGELOG.md file for people who can view changelog from github
---
`iris changelog` command:
`iris changelog [version]` (e.g. iris changelog v0.5.1)
`iris changelog -n 3` (see changelog of 3 latest releaseas)
=> `iris changelog` looks just like CHANGELOG.md, grouped headers and
all. It only stops looking that way if we personally overwrite that
release's description with something else
=> With 1-hour local cache (invalidated on updater-channel change),
`--refresh` flag to bypass, graceful fallback to stale cache on GitHub
rate-limiting
---
- New auto update config
```toml
# 0 = off (default, notify only), 1 = auto-install, 2 = always confirm first
auto-update = 1
```
- New just debug-changelog and just debug-autoupdate recipes (fully
offline, mocked HTTP servers, no real network/config touched)
Fixes#124.
Since #78 the shell reports `IRIS_CWD` on `chpwd`, but the wrapper only
passes it to
`spec.SetCWD` for suggestions. Its own working directory never moves, so
tools that
read the pane's foreground process to locate the shell resolve the wrong
path after a
`cd`. I hit this with herdr, which stopped detecting the git repository
of a pane once
IRIS was in it. tmux uses the same lookup for `pane_current_path`.
The absolute path check matches what `spec.SetCWD` already does, so both
treat a
malformed value the same way. The `os.Chdir` error is dropped because
the path comes
from the shell integration, the user cannot act on the failure, and
keeping the last
known directory is better than stopping the loop.
`go test ./...` passes. I tested this end to end on macOS only: the
wrapper followed
the `cd`, and herdr picked the repository back up.
One limitation: the outer launcher process keeps its start directory,
only the wrapper
running `runWrapper` follows. That was enough for herdr, which reads the
wrapper.
closes#62
when you open a new Zellij pane, the shell inherits Iris’s environment
variables, so it thinks Iris is already running. But the pane doesn't
inherit the underlying connection (FD 13), so suggestions silently fail
into a dead pipe
the old code only checked for `tmux` by process name, ignoring Zellij
completely
- Fixed:
instead of checking for specific app names, Iris now tracks the active
terminal device (`IRIS_TTY`)
if a new shell detects that it's on a different `TTY` than Iris, it
clears the stale environment variables so Iris can start fresh. The
`IRIS_PID != PPID` check prevents Iris from trying to re-exec itself
when spawning sub-shells.
```sh
if [ -n "$IRIS_PID" ] && [ "$IRIS_PID" != "$PPID" ] && [ "${TTY:-$(tty 2>/dev/null)}" != "$IRIS_TTY" ]; then
unset IRIS_PID IRIS_IS_CHILD IRIS_FD IRIS_TTY
fi
```
closes#122
please read the conversation to understand this change
The bug was that the reset-mode-to-config-default logic already existed
in the code (activeMode = loadMode()) but sat in a switch case that
could never execute, the bytes it matched (\r, \n, Ctrl+C, Ctrl+U) were
already intercepted and handled earlier in the same loop iteration with
an unconditional continue, so control never reached that switch
## Summary
Closes#108
This PR fixes two issues related to terminal interaction and rendering:
- Make Right Arrow ghost acceptance configurable instead of hard-coded.
- Correct overlay positioning when the shell prompt wraps across
terminal lines.
## Changes
### Right Arrow configuration
- Add a new `navigate-right` keybinding to the configuration.
- Add `navigate-right = "right"` to the default configuration to
preserve existing behavior.
- Update the wrapper to use the configured binding instead of a
hard-coded Right Arrow check.
- Forward the Right Arrow escape sequence to the PTY when
`navigate-right` is unset, allowing the shell or terminal to handle it
normally.
### Overlay positioning
- Compute the cursor's current column from the total visual width
instead of using the accumulated width directly.
- Correctly handle wrapped prompts by calculating the cursor column
modulo the terminal width.
- Clamp the overlay position so it never extends beyond the terminal
width.
- Extend debug logging with the computed cursor position to aid
troubleshooting.
## Why
Right Arrow ghost acceptance was previously implemented as a hard-coded
behavior, making it impossible to disable or remap through
configuration. This change makes it consistent with the existing
configurable keybindings while preserving the default behavior.
Overlay positioning assumed that the prompt length represented the
cursor's current column. When the prompt wrapped onto a new terminal
line, this assumption caused the suggestion overlay to be positioned
incorrectly. Computing the cursor column from the wrapped position
ensures the overlay is aligned with the actual cursor.
## Testing
- [x] Default configuration retains Right Arrow ghost acceptance.
- [x] Setting `navigate-right = ""` forwards Right Arrow to the shell.
- [x] Existing keybindings continue to function as before.
- [x] Overlay is correctly positioned for both short and wrapped
prompts.
- [x] Overlay remains within terminal bounds.
Fixes#88
## Root cause
`MatchKey` treated `Ctrl+M` and the Enter key as the same raw byte
(`0x0d`). After editing the generated config, a `ctrl+m` binding could
shadow Enter, causing Return to toggle mode instead of executing the
selected command.
While investigating this, I also found that `MatchKey` only recognized
raw control bytes for `ctrl+<letter>` bindings. In Fish and terminals
that use the Kitty keyboard protocol (e.g. Kitty and Foot), those key
combinations may instead be sent as CSI sequences such as `\x1b[106;5u`,
preventing navigation bindings like `ctrl+j` / `ctrl+k` from being
matched.
## Changes
* `internal/config/keys.go`
* Reserve the Enter byte (`0x0d`) so it cannot be claimed by a `ctrl+m`
keybinding.
* Add `matchKittyCtrl()` to support Kitty keyboard protocol CSI
sequences for `ctrl+<letter>` bindings when the Ctrl modifier is
present.
* `root/wrapper.go`
* Extract `handleNavKey()` to remove duplicated navigation handling
logic.
* `internal/config/config_test.go`
* Add regression tests covering the Enter/`ctrl+m` fix and Kitty
keyboard protocol key sequences.
## Tested
* `go test ./...` — 201 passed
* Verified that Enter is no longer shadowed by `ctrl+m` after editing
the generated config.
* Verified that `ctrl+j` / `ctrl+k` navigation works correctly in Fish.
---------
Co-authored-by: verse91 <versedev.store@proton.me>
Co-authored-by: VERSE <sunnyone0901@gmail.com>
closes#57
IRIS now automatically read and suggest internal configuration aliases
for Git (`.gitconfig`, `.git/config`) and Cargo (`.cargo/config.toml`)
- Added the `Provider` interface and extended registry for CLI tools
that define aliases
- Git: parse aliases via `git config --show-scope`, supporting raw
tab/space separation and scope differentiation
- Cargo: traverse the directory tree upwards to root to read the
`.cargo/config.toml` configuration
- 0ms hot-path caching: key caching is calculated entirely via `os.Stat`
mtime (no subprocess spawn) combined with a 2-second TTL window to
ensure 0ms latency when typing
- Lookup integration: display alias suggestions at the subcommand level
and automatically expand aliases as the user enters further arguments
(ignore shell pipeline commands starting with `!`)
closes#67
So I made an auto-exec confg `auto-exec = true`
(e32c0ba6ed69f3c764353e58b8fbacf160f37d5a) which means:
- If you want your command in the prompt to be executed right away, you
can set this = false (for example, you are typing `nvim ~/.conf` but
IRIS is suggesting `nvim ~/.config`, it will ignore the suggestion and
exec what you are typing, this is current default behavior)
- If you want to auto execute full command which is being suggested, you
can set this = true (this is the opposite of above, when you are typing
`nvim ~/.conf` but IRIS is suggesting `nvim ~/.config`, it will auto
exec `nvim ~/.config` instead, but sometime you want to exec what you
are typing instead of the suggestion right?, just press esc or shift tab
to hide the menu and it will)
---
Select keybinding to replace tab
(0dc01cba8c610e8d6fd8c50f6a71bdcc061a098f)
`select = "ctrl+y"`
Some users may love nvim keybinding or emacs, so I bring navigation
keybinding to be configurable (0dc01cba8c610e8d6fd8c50f6a71bdcc061a098f)
```
navigate-up = "ctrl+k"
navigate-down = "ctrl+j"
```
---
I also added a option for UI width
(dfa3b810a7be406fe4652b3886020f37bb166811)
`max-width = 80`
- If you configure `max-width` to be larger than the actual width of the
current terminal, Iris will automatically "compress" the window to match
the terminal's width (meaning it will never overflow and break the
interface)
- If you configure `max-width` too small (e.g 10), the interface will
have cramped text, making it look very bad. So, I've set a minimum safe
min limit of 40. If the configured value is smaller than this, Iris will
automatically use 40
Small note: If you set `max-width = 0` (or leave it blank), it will
automatically use Iris's default standard size of 76. SO you can
confidently customize it without fear of breaking the UI (I hope so
lmao)
---
Added a fallback to ensure Iris never crashes or loses shortcuts if the
user accidentally deletes keybinding configuration lines or
intentionally assigns empty strings " "
(f682c9bf623f3ed46ad0763221145604dbd91b07)
Fix typo `toggle_mode` to `toggle-mode`
(b39be6673e552735fa65a6c9807510c83b4d9cdd)
if you have a better config design, please tell me, I always listen to
your idea
## Summary
- synchronize the shell current working directory with Iris over the
existing IPC pipe
- use the synchronized directory for file, workspace, Git, and history
suggestions on macOS
- preserve the Linux `/proc/<pid>/cwd` and process-CWD fallbacks
- update zsh, bash, and fish integrations while preserving command exit
status
## Problem
On macOS, `/proc/<pid>/cwd` is unavailable, so `GetCWD` fell back to the
directory where Iris was launched, usually `$HOME`. After changing
directories in the shell, file suggestions could therefore continue to
come from `$HOME`.
## Tests
- `go test -race ./spec`
- `go test ./root -run ^'$'`
- `go test ./... -run ^'$'`
- generated zsh, bash, and fish hook syntax checks
Co-authored-by: Pavel Naumov <Pavel.Naumov@berekebank.kz>
Closes#76
Session history was recorded only after receiving `IRIS_CMD_STOP`.
Directly launched Bash sessions may not install that completion hook,
and Bash does not flush `.bash_history` until the shell exits.
There was also a race where `Enter` was sent to the shell before Iris
stored the submitted command, allowing fast commands to finish before
the command was captured.
The fix is
- Record commands in session history immediately when they are
submitted.
- Store the submitted command before sending Enter to the shell.
- Keep command-completion handling responsible for exit-code and
frecency data.
Once again (and please tell me if you prefer not) I'm not fluent in `go`
so this was all Sol
Fix#65
when the user presses Enter to run a command, Iris sets `isCommandActive
= true` so it knows a command is running and skips rendering the
overlay. After the command finishes, `isCommandActive` needs to reset to
`false` for Iris to work again. In zsh/fish, preexec/precmd hooks send
an `IRIS_CMD_STOP` signal over the IPC pipe, which Iris receives to
reset `isCommandActive = false`. Bash has no equivalent hook, so
`IRIS_CMD_STOP` is never sent and `isCommandActive` stays stuck at true
forever
so I added a bash-only fallback inside `isExecuting()`: if
`isCommandActive` is true but `TIOCGPGRP` (a kernel API) confirms the
shell's process group is back in the foreground (meaning the command
finished), automatically reset `isCommandActive` to false. zsh/fish
still rely on IPC signals as before
also added a `PROMPT_COMMAND` hook to iris init bash to `send
IRIS_CMD_STOP` after every command (belt-and-suspenders), though that's
not the main fix
### Summary of Changes
Fixes **#53** (*Fish shell integration panics with `inappropriate ioctl
for device`*).
#### 1. Fixed Non-TTY Stdin (`panic: inappropriate ioctl for device`) —
Fixes#53
- **Root Cause**: When executing `iris init fish | source` (or sourcing
shell configs), `os.Stdin` (FD 0) is connected to a pipe instead of a
TTY. Calling `term.MakeRaw()` on
a pipe returns `ENOTTY`.
- **Fix**: Guarded `term.MakeRaw()` with `term.IsTerminal()` checks
across `root/wrapper.go` and `root/root.go`. Added fallback to open and
use `/dev/tty` when `os.Stdin` is
not a terminal device.
- **Fish IPC Hooks**: Added `fish_preexec` and `fish_postexec` event
handlers to `iris init fish` to send `IRIS_CMD_START` / `IRIS_CMD_STOP`
IPC signals over `$IRIS_FD`.
#### 2. Fixed Shell Setup Order (Duplicate `fastfetch` / startup
executions) — Fixes#53
- **Root Cause**: `iris setup` appended the init script to the bottom of
shell configs (`config.fish`, `.zshrc`, `.bashrc`), causing pre-existing
interactive startup commands
to run twice.
- **Fix**: Updated `setupCmd` in `root/init.go` to **prepend** `# Iris
Autocomplete` to the top of shell configuration files.
#### 3. Fixed Terminal Exit Loop Handling — Relates to #50
- **Root Cause**: When typing `exit` in the wrapped shell, Linux PTY
returns `syscall.EIO` (`Input/output error`) on `ptmx.Read()` rather
than `io.EOF`. `runWrapper()` was
ignoring non-EOF errors and looping indefinitely without restoring the
terminal or calling `os.Exit(0)`.
- **Fix**: Updated the `ptmx.Read` error handler in `root/wrapper.go` to
handle `syscall.EIO` and exit cleanly.
---
### Issues Addressed
- Closes#53
- Relates to #50, #55
---
### Verification Tested
- [x] Verified `iris setup fish` / `iris init fish | source` initializes
without `ioctl` panic.
- [x] Verified typing `exit` in Fish, Zsh, and Bash terminates IRIS
cleanly and restores standard terminal state.
- [x] Ran `go test ./...` — all tests pass.
---------
Co-authored-by: verse91 <versedev.store@proton.me>
Co-authored-by: VERSE <sunnyone0901@gmail.com>
## Add
Make these things configurable
- Expand aliases
- Suggest hidden files (#59)
- Toggle menu and mode
```
[ui]
hidden-files = true # or false
[core]
expand-alias = true # or false
[keybindings]
toggle-mode = "ctrl+r"
toggle-menu = "ctrl+space"
```
- Auto reload config on changes: now just changes config and save the
file, IRIS will auto detect and reload in silent
- Added all these in config on init and docs
## Fix
I tested on holding backspace and the ghost text keep glitching so I
fixed it, this is just a small fix
- add transition scoring engine to learn sequential workflows per
directory using command skeletons
- prioritize current active git branch over older branches in
suggestions
- restore strict chronological order for history navigation and bypass
AI re-ranking
- add in-memory session history for instant access to just-run commands
- fix PTY prompt not syncing when using arrow keys in the history menu
- fix history tie-breaker to properly prioritize newer commands by
assigning larger IDs
- fix potential mutex deadlock between bufferMu and pty write during
history navigation
- fix workspace git detection to remove depth limits and correctly
identify repositories with detached HEADs
- fix SQLite connection leaks in frecency queries
- fix global state leaks in history tests by snapshotting and restoring
registry states
- modernize string prefix checks using strings.CutPrefix
When you paste text in most modern terminals (Alacritty, iTerm2, Kitty,
WezTerm...), the terminal enables bracketed paste mode and wraps the
pasted content between two control strings:
`\x1b[200~` --> start pasting
`\x1b[201~` --> end pasting
Previously, iris detected these two strings but didn't track the state.
Now I have:
Declared `inBracketedPaste := false` before the stdin loop
Set `inBracketedPaste = true` when receiving `\x1b[200~`, `false` when
receiving `\x1b[201~`
Checked `!inBracketedPaste` before triggering the alias expansion
reset `inBracketedPaste` on enter/ctrl-c/ctrl-u and when executing
Enhance lookup mechanics and priority propagation (`spec/lookup.go`)
Remove the requirement of typing a leading `-` when searching for
options, allowing flag suggestions to surface naturally during input
Accurately forward the `Priority` field from registry specs into the
`Suggestion` struct so the scoring engine evaluates relevance properly
Integrate scoring into the main suggestion pipeline
(`root/suggestions.go`)
Collect context signals using `scoring.CollectSignals(cwd, query,
rootCmd, store)` with the live working directory retrieved from
`spec.GetCWD()`
Pass the entire deduplicated list across spec, history and AI items
through `scoring.Score(deduped, signals)` to compute scores and sort by
descending order before rendering on screen
Record execution history in Frecency Store (`root/wrapper.go`)
Save the last executed command to `lastSubmittedCommand` whenever Enter
is pressed or a suggestion is selected
Trigger `store.Record(...)` asynchronously within the `IRIS_CMD_STOP`
hook using a dedicated goroutine guarded by panic recovery and timeout
limits, preventing blocks on the main shell thread
Optimize workspace identification for AI
(`internal/ai/context_provider.go`)
Use `workspace.DetectCached(cwd)` to instantly fetch ecosystem context
across Git, Node, Go, Rust, Python and Docker alongside active git
branch state
This caching mechanism avoids redundant subprocess execution when
outside git repositories, significantly speeding up prompt context
generation
This project is starting to grow, and I've seen flaws in the initial
project structure design
I think I'll restructure the entire project directory for better
scalability and also cleaner
## Feat
* feat(ai): add AI suggestion engine, context gathering, and ghost text
overlay (`v0.3.0`)
## Security & Perf
* fix(ai): restrict background `--help` execution to a hardcoded command
allowlist to prevent RCE
* fix(root): defer context cancellation in goroutine to prevent resource
leaks
* perf(ai): implement LRU eviction and maximum size limit for
`ProviderCache`
* perf(ai): truncate command context output
(`CommandContextProvider.Gather`) to 1000 characters to save tokens
## Bug Fixes
* fix(ai): add mutex synchronization and thread-safe snapshotting to
`AIEngine.RegisterProvider` and `GatherDynamicContext`
* fix(ai): return a copy of fresh `Suggestion` in `AIEngine.Suggest` to
prevent cache mutation
* fix(ai): use length-prefixed encoding in `EnvSnapshot.Hash` to prevent
delimiter collisions
* fix(ai): skip variable assignment lines containing `=` when extracting
Makefile targets
* fix(runner): check `scanner.Err()` and return `nil` on scan errors in
justfile generator
* test(ci): rename `git commit` to `git checkout` in overlay test to
resolve CI `typos` false positive
- I think it would be better if one for commands only, one for the spec
handling core logic so people won't be confused by the core logic inside
the commands folder (cleaner I guess)
- Regen docs
This pull request modernizes the codebase by adopting Go 1.21+ features, including built-in functions like min and max, standard library iterators such as strings.SplitSeq and strings.FieldsSeq, utility functions like maps.Copy and slices.Contains, and string manipulation helpers like strings.Cut and strings.CutPrefix. Feedback is provided on a test file where the usage of strings.Builder can be further optimized to avoid unnecessary string allocations during concatenation.
`go fix -diff ./...`
- correct nightly release tag name
989b61eefb212c18ca04b2121b382f19f86e39c2
- iris not start in tmux 0fef95430b6a1877810e3a4ffd8aa658f75c542c
- suggestions only show on full word typing
f29cbfd74a2d90371f8f369e6f8e7419f979da38
- keep current dir on reload instead of return home dir
a923e78f52a95e06587aff6856a6a81319b38e84