docs: update docs (#43)

- macOS, Linux badge and AI suggestions showcase in README
- Update user guide and docs for development
This commit is contained in:
VERSE
2026-07-27 19:50:28 +07:00
committed by GitHub
parent 1540b9655a
commit 078e5e817c
13 changed files with 490 additions and 333 deletions
+15
View File
@@ -0,0 +1,15 @@
# Iris developer & architecture documentation
This directory contains code architecture guides, engine design notes, and development instructions for Iris contributors.
## Table of contents
- [Development workflow & runner](development.md): Environment setup, building, testing, linters, and modular `just` runner (`justfiles/`)
- [AI engine architecture](ai.md): Internal AI completion system (`internal/ai`), providers, prompt design, and debounce strategy
- [Scoring & frecency engine](scoring.md): Scoring mechanics (`internal/scoring`), frecency calculations, context rules, and skeleton extraction
- [Overlay & rendering pipeline](overlay.md): PTY overlay positioning, cell width calculation, and terminal rendering
- [PTY bridge & keystroke wrapper](root.md): Keystroke interception loop, raw mode PTY handling, and IPC scanner
- [Spec & completion engine](spec.md): Static specifications, priority-based flag gating, and Cobra `__complete` dynamic completion
- [File & path generator](filegen.md): File system traversal, extension filtering, and directory slash preservation
- [History provider](history.md): Shell history indexing, search algorithms, and caching
- [Auto updater](updater.md): Release tracking, version comparison, and atomic binary updates
+29
View File
@@ -0,0 +1,29 @@
# AI engine architecture (`internal/ai/`)
The AI subsystem provides real-time, context-aware command completions powered by cloud or local LLM providers (Groq, Ollama).
## Core components
- `internal/ai/client.go`: Defines the `Client` interface and handles HTTP requests to OpenAI-compatible chat completion endpoints.
- `internal/ai/env.go`: Captures runtime context snapshots (`EnvSnapshot`) including CWD, previous command, exit code, and recent history.
- `internal/ai/prompts.go`: Constructs system and user prompts optimized for shell command completion.
- `root/wrapper.go`: Manages async debounce timers, request cancellation (`context.WithCancel`), and ghost text injection.
## Provider interface
All providers use a unified HTTP pattern matching OpenAI's `/v1/chat/completions` format:
```go
type Client interface {
Suggest(ctx context.Context, prompt string, env *EnvSnapshot, currentCmd string) (*AISuggestion, error)
}
```
## Request lifecycle
1. User types in the prompt buffer.
2. `root/wrapper.go` triggers a debounce timer (`debounce_ms`, default 500ms).
3. If typing continues, previous in-flight contexts are cancelled (`aiCancel()`).
4. An `EnvSnapshot` is created capturing CWD, last executed command, and up to 3 recent history entries.
5. Provider sends an HTTP POST request with structured JSON payload.
6. Response is parsed and rendered as inline ghost text via `overlay.InjectAISuggestion()`.
+50
View File
@@ -0,0 +1,50 @@
# Development guide
This document outlines the development workflow, build system, and coding conventions for contributors working on the Iris codebase.
## Environment setup
Clone the repository and sync dependencies:
```bash
git clone https://github.com/versenilvis/iris.git
cd iris
go mod tidy
```
## Modular `just` command runner
We use `just` as our primary command runner. Recipes are organized into modular files under `justfiles/`:
- `justfiles/build.just` (`[build]` group): `build`, `optimized-build`, `build-release <version>`
- `justfiles/dev.just` (`[dev]` group): `run`, `config-init`, `reload`, `copy`
- `justfiles/test.just` (`[test]` group): `test`, `lint`, `analyze` (alias `ana`)
- `justfiles/gen.just` (`[gen]` group): `gen-docs`
- `justfiles/pkg.just` (`[pkg]` group): `pkg`
- `justfiles/debug.just` (`[debug]` group): `debug`, `debug-update`, `debug-notify`, `debug-install`
Run `just -l` to quickly view all available recipes organized by group.
## Building and testing
### Rapid hot-reload
```bash
just reload
```
### Running unit tests
```bash
just test
# OR
go test -v ./...
```
### Running linter
```bash
just lint
# OR
golangci-lint run ./...
```
+30
View File
@@ -0,0 +1,30 @@
# Dynamic file suggestion generator (`spec/filegen.go`)
The `FileGenerator` is a dynamic suggestion engine bridging static command specs with your live filesystem.
## How it works
The generator is assigned to commands or options expecting file path arguments (e.g. `cat`, `go run`).
### Path resolution
Handles both local and nested paths:
- `cat m` -> Scans `./` for entries starting with `m`.
- `cat src/` -> Scans `src/` for all entries.
- `cat src/main` -> Scans `src/` for entries starting with `main`.
### Intelligence features
1. **Extension filtering**: Restricts the generator to specific file extensions (e.g. `FileGenerator(".go")` for `go build`).
2. **Directory suffixing**: When a directory is suggested, Iris appends `/` without an extra trailing space, allowing continuous folder traversal.
3. **Descriptions**: Converts file extensions into human-readable labels (e.g. `.mp4` -> `video`, `.zip` -> `archive`).
4. **Directory-only mode**: Commands like `cd` filter out files to display directories only.
## Code example
```go
// registration for 'go run'
Generator: spec.FileGenerator(".go")
```
When typing `go run `, Iris scans the target directory, filters for `.go` extensions, and ignores unrelated binaries or assets.
+22
View File
@@ -0,0 +1,22 @@
# Shell history integration (`integration/history.go`)
The `history` module provides `Ctrl+R` search functionality by reading the user's persistent shell command history.
## Features
- **Zsh extended support**: Parses `: <timestamp>;<command>` format common in Zsh.
- **Lazy loading**: Reads disk only when history search is invoked, preserving instant startup performance.
- **Fuzzy search**: Integrates with the `fuzzy` search engine for match scoring.
- **Deduplication**: Filters duplicate commands, displaying unique entries.
## Data flow
1. User presses `Ctrl+R`.
2. `root` sets `mode = "history"`.
3. `SearchHistory("")` is called.
4. If cache is empty:
- Reads history file (`~/.zsh_history` or `~/.bash_history`).
- Strips metadata using delimiters.
- Populates command memory cache.
5. Matches are scored and sorted by the search engine.
6. Suggestions are returned to `overlay` for rendering.
+39
View File
@@ -0,0 +1,39 @@
# Overlay rendering UI (`integration/overlay.go`)
The `overlay` package handles the visual representation of suggestions. It is designed to be non-destructive, drawing over the shell without corrupting the prompt or scrollback buffer.
## Design philosophy
- **ANSI ESC everywhere**: Uses ANSI escape sequences and CSI (Control Sequence Introducer) codes to manipulate terminal positioning.
- **Save and restore**: Uses DECSC (`\0337`) and DECRC (`\0338`) escape sequences to return to prompt anchor after drawing.
- **Fixed width**: Menu box uses a standard 72-character width (`boxWidth`) for consistent layout rendering.
## Technical details
The overlay engine manages positioning, scrolling protection, and styled cell drawing directly on the terminal grid.
### Terminal scrolling protection
When rendering near the bottom of the screen, printing new lines causes terminal scrolling which buries the active prompt. Iris prevents this by:
1. Moving to the prompt anchor.
2. Printing N empty newlines to pre-allocate scroll space.
3. Moving back up N lines.
4. Re-saving the cursor at this stabilized location.
### Styling with Lipgloss
While Iris handles cursor positioning via raw ANSI codes, visual component styling uses Lipgloss:
- **Colors**: Dracula-inspired color theme (`#BD93F9`, `#6272A4`).
- **Icons**: Fixed-width columns for category badges and completion types.
- **Selection**: Highlights the currently active suggestion item with a distinct background (`#44475A`).
## Example logic
To render line 1 of the menu dropdown:
1. `\0338` (Jump to prompt anchor).
2. `\033[2B` (Move 2 lines down).
3. `\033[K` (Clear line buffer).
4. Print formatted item: `│ [Icon] Command name... │`.
+39
View File
@@ -0,0 +1,39 @@
# Iris central integration & event loop (`root/`)
The `root` package is the entry point and orchestration layer of Iris, handling low-level terminal PTY manipulation and the main interaction loop.
## How it works
1. **PTY wrapper**: Starts a pseudoterminal (PTY) wrapping the user's shell (`zsh`, `bash`, or `fish`). Shell is selected via `--shell` flag, config, or auto-detected by walking `/proc/<pid>/comm` up the parent process tree, falling back to `$SHELL`, and ultimately defaulting to `bash` if nothing matches.
2. **IO interception**: Runs two pumps:
- **Output pump**: Forwards shell output to terminal screen via synchronized `TermWrite`.
- **Input pump**: Listens to keystrokes in raw mode, tracks typed characters in `naiveBuffer`, and triggers suggestion rendering.
3. **State management**: Tracks active completion mode (`spec` vs `history`). Shell-specific IPC hooks (`preexec`/`precmd` for zsh, `PROMPT_COMMAND` for bash, `fish_postexec` for fish) signal command boundaries via `IRIS_CMD_STOP`.
## Key components
### `root/wrapper.go`
Contains the core PTY loop:
- Sets terminal to raw mode to intercept keys like `Tab`, `Esc`, or `Ctrl+C`.
- Manages `naiveBuffer` string tracking prompt input state.
- Handles suggestion insertion when pressing `Tab` or `Enter`.
### `root/term_sync.go`
Provides `TermWrite`, a thread-safe stdout wrapper using `sync.Mutex` to prevent screen garbling when shell output and overlay rendering overlap.
## Example flow
1. User types `g`.
2. `root` captures `g`, appends to `naiveBuffer`.
3. `root` calls `renderOverlay()`.
4. `renderOverlay` calls `Lookup("g")`.
5. `overlay` renders suggestions box.
6. User presses `Tab`.
7. `root` inserts completion into prompt buffer.
## Hot-reload
Iris includes an atomic hot-reload mechanism for rapid local development:
- **Signal listener**: The root process listens for `SIGUSR1`.
- **Process replace**: Uses `syscall.Exec` to replace the running binary in-place without killing the underlying PTY shell session.
+28
View File
@@ -0,0 +1,28 @@
# Scoring & ranking architecture (`internal/scoring/`)
The scoring engine ranks suggestions by combining frecency algorithms, workflow sequence learning, and item-type priority rules.
## Core concepts
### 1. Frecency calculation (`internal/scoring/frecency.go`)
Frecency combines execution **frequency** with **recency** decay:
$$\text{Score} = \text{Frequency} \times e^{-\lambda \Delta t}$$
Commands executed recently receive a higher score multiplier that decays over time.
### 2. Workflow sequence learning (`internal/scoring/context_rules.go`)
Iris tracks sequential command pairs to learn common developer workflows (e.g. `git add` $\rightarrow$ `git commit`, `go build` $\rightarrow$ `./iris`). When a parent command skeleton matches the previous command, related suggestions receive a priority boost.
### 3. Skeleton extraction (`internal/scoring/skeleton.go`)
`ExtractSkeleton(cmd)` normalizes full command strings into structural skeletons by removing specific arguments and flags (e.g. `git commit -m "feat: test"` $\rightarrow$ `git commit`).
### 4. Spec priority & flag gating (`spec/lookup.go`)
Within spec completion mode:
- Files and subcommands default to standard priority (`Priority = 30`).
- Flags and options default to low priority (`Priority = 10`) when typing arguments.
- When the user explicitly types `-` or `--`, flags are promoted (`Priority = 80`).
+37
View File
@@ -0,0 +1,37 @@
# Command specification & lookup engine (`spec/`)
The `spec` module is the language parser of Iris. It understands the relationship between commands, subcommands, and flags.
## Data structures
- **`Spec`**: Top-level command definition (e.g. `git`).
- **`Subcommand`**: Recursively defined children (e.g. `commit` under `git`).
- **`Generator`**: A function providing dynamic content (e.g. file paths, docker IDs).
## The `Lookup` algorithm
The core path-traversal function:
1. **Tokenization**: Splits `"git commit -m"` into `["git", "commit", "-m"]`. Empty tokens (from trailing space) indicate the user is ready for the next suggestion level.
2. **Tree walking**: Starts at the root node (`git`) and matches each token against available subcommands.
3. **Context identification**: When traversal stops (partial word or option prefix), it defines:
- `prefix`: the path already traversed.
- `partial`: the word currently being typed.
4. **Result collection**: Gathers all subcommands and options matching the `partial` prefix.
## Example
Input: `git com`
1. Tokens: `["git", "com"]`.
2. Walk: root is `git`.
3. Next token `com` doesn't match `commit` exactly.
4. Stop. `partial = "com"`.
5. Suggestions: subcommands of `git` starting with `com`.
6. Return: `git commit`.
## Shell aliases & priority
- **Dynamic alias parsing**: Scans shell config files (`.bashrc`, `.zshrc`) for defined aliases.
- **Highest priority**: Aliases appear above spec and system command suggestions.
- **Token injection**: When a root command is an alias (e.g. `gr` for `go run`), Iris injects expanded tokens into the lookup engine for accurate subcommand suggestions.
- **Display**: Shows the expanded command (e.g. `tmux a -t`) with the alias name in the description (e.g. `alias: ta`).
+97
View File
@@ -0,0 +1,97 @@
# Versioning and updates
Iris has a built-in update notification system designed to be non-intrusive and zero-latency
The update system is designed to keep Iris up to date while staying out of the way. It performs an asynchronous network check when the shell starts and notifies you exactly once per version after you run a command. This prevents redundant notifications and ensures that you only see an update message when a new release is actually available. The system also includes dedicated debugging tools to verify the notification logic without requiring real releases
## How it works
1. **Background check**: every time you open a new terminal, Iris launches a background goroutine to check for updates
- checks the network at most once every 6 hours to avoid GitHub API rate limiting
- has a 5 second timeout so it never hangs on a slow or missing network connection
- if there is no network, it fails silently with zero impact on startup
2. **State persistence**: state is stored in `~/.iris/update_state.json` with two fields:
- `last_check` - unix timestamp of the last network check
- `seen_version` - the latest version the user was already notified about
3. **Smart notification**:
- the notice only appears after you run your first command (triggered by the `IRIS_CMD_STOP` IPC signal from the shell hook)
- appears only once per session, never again even if you keep the terminal open
- if you have already been notified about a specific version, it will not show again until a newer GitHub release tag is detected
- when `iris update` is run successfully, the `seen_version` flag is cleared
## Build-time versioning
Iris uses Go `ldflags` to inject the version string at build time:
```bash
go build -ldflags="-X github.com/versenilvis/iris/root.Version=v1.2.0" -o iris main.go
```
If not provided, the version defaults to dev. The dev version will never trigger an update notification
## Commands
- `iris version` - print the current version of the running binary
- `iris update` - manually check for and apply the latest release
## Debugging and testing
There are two separate things to test: the update command itself, and the in-session notification banner
### Test 1: update command
Tests version fetching, comparison and the output message. No full Iris session needed
```bash
just build-release v0.0.1
just debug-update v1.99.0
```
Expected output
```
--- testing iris update command ---
checking for updates (current: v0.0.1)...
[IRIS] updating v0.0.1 -> v1.99.0
running: curl -sS https://raw.githubusercontent.com/versenilvis/iris/main/scripts/install.sh | sh
[IRIS] restart your terminal to use the new version
```
### Test 2: in-session notification banner
Tests the yellow notice that appears after you run your first command inside a live Iris session. Requires `iris.zsh` to be active in the inner shell so `IRIS_CMD_STOP` fires through the IPC pipe
```bash
just build-release v0.0.1
just debug-notify v1.99.0
```
Inside the new session, run any command. Expected output after the command
```
[IRIS] new version v0.0.1 -> v1.99.0 available, run iris update to upgrade
```
### Environment variables
| Variable | Purpose |
| -------------------------- | ----------------------------------------------------------------------------------- |
| `IRIS_UPDATE_URL` | override the GitHub API endpoint with a custom URL (used by `debug-update`) |
| `IRIS_MOCK_LATEST_VERSION` | skip network entirely, resolve to this version immediately (used by `debug-notify`) |
### State reset
To force a fresh network check on next launch, delete the state file:
```bash
rm ~/.iris/update_state.json
```
## Implementation details
| File | Purpose |
| ----------------- | ---------------------------------------------------------------------------------------- |
| `root/version.go` | holds the `Version` constant, defaults to `dev` |
| `root/update.go` | all update logic: state persistence, network fetch, version compare, commands |
| `root/wrapper.go` | wires the background check into the IPC loop, prints the notice on first `IRIS_CMD_STOP` |