A modern, intelligent terminal for macOS, Linux and Windows with autocomplete superpowers.
Features • Installation • Editor • Shell Setup • Development • Shortcuts • Contributing
FigyTerm is a fully local desktop terminal built with Tauri 2, React, and Rust. It brings IDE-level autocomplete to your command line — context-aware suggestions for git, docker, pnpm, npm, uv, and more — all running natively with zero network dependency.
Inspired by Fig (now part of AWS), FigyTerm is an open-source alternative that keeps your terminal experience fast, private, and extensible.
- Intelligent Autocomplete — Context-aware suggestions for commands, subcommands, options, and file paths
- Spec-based Engine — Compatible with Fig's spec format for community-driven command completions
- Split Panes — Up to 4 resizable terminal panes per tab (Cmd+D / Cmd+Shift+D)
- Multiple Tabs — Browser-style tab bar with drag-to-reorder and rename support
- Embedded Browser — In-app browser modal with tabs, address bar, and back/forward/reload (
⌘⇧B); uses a native child webview so real sites load (not an iframe). On Linux it's positioned through agtk::Fixedof our own, since Tauri can't place child webviews on GTK (tauri#10420) - Embedded Code Editor — A real editor beside the shell (
⌘⇧E): CodeMirror 6, file tabs, a breadcrumb, and a resizable file tree. Opt-in language server support for diagnostics, hover, completion, go-to-definition and rename, using the servers you already have installed. Click apath:line:colin terminal output and it opens there. Atomic saves with conflict detection, CRLF and BOM preserved, crash-safe drafts. Workspaces remember their own open tabs; fuzzy file finder (⌘P) and streamed project search (⌘⇧F). Loaded on first open, so it costs nothing at launch — see the design notes - Git in the Editor — Changed files badged in the tree, changed lines marked in the gutter, a branch indicator in the status bar, and a GitHub Desktop-style panel: tick the files, write a summary, commit. A history tab where a commit opens into a drawer with its message and files, plus fetch and push. Discards to the trash, and runs your own
git, so your hooks and credential helper apply - A Real Diff Viewer — Unified or split, with word-level highlighting inside changed lines, and five presets (GitHub, GitLab, VS Code, delta, plain
git diff) so it reads like the tool you already use - Markdown Preview — GitHub-flavoured rendering with a live outline, scroll synced both ways, and clickable in-page and sibling-file links. No
dangerouslySetInnerHTMLanywhere, so a document can't inject markup - Command History Search — Fuzzy-search past commands with picture-in-picture mode (
⌘R) - System Monitor — Live CPU and memory charts in a draggable modal (
⌘⇧M) - Terminal Search — Find text in the active pane (
⌘F) - Command Palette — Every action the app has, searchable, each spelled for the platform you're on (
⌘⇧P) - Oh My Zsh Integration — Real-time theme switching with full prompt rendering
- Dark & Light Mode — Beautifully themed UI that adapts to your preference (
⌘⇧L) - Recent Directory Ordering — Frequently visited folders appear first in suggestions
- Clickable URLs — Links in terminal output open in your default browser
- Native Performance — Rust PTY backend with zero-latency input
macOS (Apple Silicon and Intel), Linux (x86_64) and Windows (x64) — see the installation guide for the full details.
You only install once: FigyTerm updates itself from then on. The exceptions are the
packages meant to be managed by something else — a Linux .deb/.rpm and the Windows
.msi — which stay with whatever installed them.
curl -fsSL https://raw.githubusercontent.com/code4mk/figyterm/main/install.sh | shOne line, no security dialog, no xattr step. It detects your Mac's architecture,
downloads the right build, and installs to /Applications.
Read the script first — you should read anything you pipe to a shell. It
touches a temp directory and FigyTerm.app, nothing else.
-
Download the build for your Mac from Releases:
aarch64for Apple Silicon,x64for Intel -
Open the
.dmgand drag FigyTerm into Applications -
Clear the quarantine flag macOS added during the download:
xattr -cr /Applications/FigyTerm.app
-
Launch FigyTerm
Step 3 isn't optional — skip it and macOS says "FigyTerm is damaged and can't be opened", which sounds alarming but only means the app is unsigned.
If macOS still blocks it, open System Settings → Privacy & Security, scroll down, and click Open Anyway. (On macOS 15 and later this is the only way through — Apple removed the old right-click → Open shortcut.)
curl -fsSL https://raw.githubusercontent.com/code4mk/figyterm/main/install-linux.sh | shInstalls the AppImage to ~/.local/bin with a desktop entry.
Read the script first.
Or take a package from Releases:
sudo apt install ./FigyTerm_*_amd64.deb, sudo dnf install ./FigyTerm-*.x86_64.rpm.
No Gatekeeper, no quarantine, no xattr — none of that exists here.
The AppImage is the build that updates itself; a distro package is your package
manager's to update. If the window comes up blank on Nvidia or older Mesa drivers, start
it with WEBKIT_DISABLE_DMABUF_RENDERER=1.
Download FigyTerm_*_x64-setup.exe from Releases
and run it. It installs for the current user, needs no administrator rights, and updates
itself from then on.
Windows shows "Windows protected your PC" the first time, because the installer
isn't code-signed — click More info, then Run anyway. There's no equivalent of
the macOS curl trick here: reputation accrues per binary as downloads accumulate.
An .msi is also published for Group Policy or Intune deployment. It doesn't
self-update, on purpose — a managed install stays managed.
Why does Option 1 skip that step?
FigyTerm isn't code-signed with an Apple Developer ID (it costs $99/year), so macOS quarantines it on download and refuses to open it until the flag is cleared.
Quarantine is applied by the downloading application — browsers set it, curl
doesn't. The script isn't bypassing a security check; it just isn't a browser, so the
flag is never set in the first place. xattr -cr reaches the same end state after
the fact.
Either way it's a one-time thing: the built-in updater also downloads over HTTP rather than through a browser, so updates are never quarantined.
Keychain access popup: macOS may ask to allow FigyTerm to access your keychain (for SSH keys, credentials, etc.). Click Always Allow or Allow to proceed. If you accidentally deny it, go to Keychain Access > login and update the access control for the relevant entry.
git clone https://github.com/code4mk/figyterm.git
cd figyterm
npm install
npm run tauri buildThe built .app and .dmg will be in src-tauri/target/release/bundle/macos/.
| Requirement | Version |
|---|---|
| Node.js | 18+ |
| Rust | 1.86+ |
| Tauri CLI | 2.x |
| Xcode Command Line Tools (macOS) | xcode-select --install |
| WebKitGTK dev packages (Linux) | see INSTALLATION.md |
| MSVC build tools + WebView2 (Windows) | Visual Studio 2022 C++ workload |
FigyTerm runs your normal login shell — it uses the $SHELL environment variable (same as Terminal.app). You do not need to install anything extra for basic use.
For the best experience (themes, prompts, plugins, and recent-folder ordering from history), we recommend zsh with Oh My Zsh.
| Shell | Required? | Notes |
|---|---|---|
| zsh | No (recommended) | Default on macOS since Catalina (/bin/zsh). FigyTerm launches whatever $SHELL points to. |
| bash / fish | Supported | Works out of the box if $SHELL is set to them. |
| Oh My Zsh | No (recommended) | Enables theme switching in Settings and rich prompt rendering. Without it, the terminal still works — you just won't see Oh My Zsh themes in Settings. |
To confirm your shell:
echo $SHELL
# Expected on modern macOS: /bin/zshIf you want to switch the default shell to zsh:
chsh -s /bin/zshRestart FigyTerm (or open a new tab) after changing shells.
Oh My Zsh is not required, but it unlocks FigyTerm's theme picker (Settings → Theme) and matches how most developers configure their macOS terminal.
Install Oh My Zsh:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"After install:
- Restart FigyTerm or open a new tab
- Open Settings → Theme to browse and switch Oh My Zsh themes live
- Custom themes can be placed in
~/.oh-my-zsh/custom/themes/
Already using Oh My Zsh in Terminal.app? FigyTerm reads the same
~/.zshrcand~/.oh-my-zshconfig — no extra setup needed.
Not using Oh My Zsh? FigyTerm still works. Autocomplete, split panes, tabs, and path suggestions are independent of Oh My Zsh.
| macOS | Linux / Windows | Action |
|---|---|---|
⌘ T |
Ctrl+Shift+T |
New tab |
⌘ ⇧ T |
Ctrl+Alt+T |
New tab (same directory as active tab) |
⌘ ⇧ W |
Ctrl+Shift+W |
Close active pane |
⌘ D |
Ctrl+Shift+D |
Split pane horizontally |
⌘ ⇧ D |
Ctrl+Alt+D |
Split pane vertically |
⌘ K |
Ctrl+Shift+K |
Clear terminal |
⌘ F |
Ctrl+Shift+F |
Find in terminal |
⌘ R |
Ctrl+Shift+R |
Search command history |
⌘ C |
Ctrl+Shift+C |
Copy selection |
⌘ V |
Ctrl+Shift+V |
Paste |
⌘ ⇧ B |
Ctrl+Shift+B |
Open browser |
⌘ ⇧ E |
Ctrl+Shift+E |
Open code editor |
⌘ ⇧ M |
Ctrl+Shift+M |
System monitor |
⌘ ⇧ L |
Ctrl+Shift+L |
Toggle light/dark theme |
⌘ ⇧ P |
Ctrl+Shift+P |
Command palette |
⌘ , |
Ctrl+, |
Settings |
⌘ 1-9 |
Ctrl+1-9 |
Switch to tab N |
⌘ ⇧ [ |
Ctrl+Shift+[ |
Previous tab |
⌘ ⇧ ] |
Ctrl+Shift+] |
Next tab |
Tab |
Tab |
Accept autocomplete suggestion |
↑ ↓ |
↑ ↓ |
Navigate suggestions |
Esc |
Esc |
Dismiss suggestions |
Shell-native shortcuts (Ctrl+C, Ctrl+D, Ctrl+Z, Ctrl+L, Ctrl+R) pass through
directly. That's why the second column uses Ctrl+Shift rather than plain Ctrl, the
same as GNOME Terminal, Konsole and Windows Terminal: a bare Ctrl+letter belongs to the shell, and an
app shortcut that took it would break the terminal it's wrapped around. The two chords
that would collide (Ctrl+Shift+T and Ctrl+Shift+D are already taken) fall back to
Ctrl+Alt.
| macOS | Linux / Windows | Action |
|---|---|---|
⌘ S |
Ctrl+S |
Save |
⌥ ⌘ S |
Ctrl+Alt+S |
Save all |
⌘ P |
Ctrl+P |
Go to file |
⌘ X ⌘ C ⌘ V |
Ctrl+X Ctrl+C Ctrl+V |
Cut, copy, paste |
⌘ F |
Ctrl+F |
Find in file |
⌥ ⌘ F |
Ctrl+H |
Find and replace |
↵ / ⇧ ↵ |
Enter / Shift+Enter |
Next / previous match, in the find field |
⌘ ⇧ F |
Ctrl+Shift+F |
Search in folder |
⌘ G |
Ctrl+G |
Go to line |
⌘ W |
Ctrl+W |
Close file tab |
⌘ B |
Ctrl+B |
Toggle the file tree |
⌘ / |
Ctrl+/ |
Toggle comment |
⌘ D |
Ctrl+D |
Select next occurrence |
F12 / ⌘⌥ ↓ |
F12 |
Go to definition (also ⌘-click / Ctrl-click) |
⇧ F12 |
Shift+F12 |
Find references |
F2 |
F2 |
Rename symbol |
⌘ . |
Ctrl+. |
Quick fix |
⇧ ⌥ F |
Ctrl+Shift+I |
Format document |
⌘ 1-9 |
Ctrl+1-9 |
Nth file tab |
Esc |
Esc |
Close the editor |
These use plain Ctrl off macOS, unlike the table above. The reason the app
takes Ctrl+Shift elsewhere is that a bare Ctrl+letter belongs to the shell —
and inside the editor no shell has focus, so the conventional editor chords are
free to mean what they usually mean.
Right-clicking the text gives you cut, copy, paste, select all, undo, redo,
find, go to line, save and the path actions; right-clicking the file tree gives
you the file operations. Replace is ⌥⌘F on macOS rather than the usual ⌘H,
which belongs to the system.
⌘⇧E opens an editor over the terminal, rooted at the focused pane's working
directory. It is not a second native webview like the browser: the browser
hosts pages it doesn't control, whereas the editor renders our own content, so
it's ordinary React in the app's own webview — no bounds arithmetic, no DPI
factor, no platform-specific container, and identical behaviour on all three
platforms. The design notes go into why that difference
matters.
It has its own settings — the gear in the toolbar. Font, size and line height are the editor's, not the terminal's, since code and a shell rarely want the same one; leave them blank and they follow the terminal. Indentation guides, word wrap, bracket closing, word completion, language servers, hidden files and the diff style all live there too, and every control applies as you change it.
Editing is CodeMirror 6 — multi-cursor, folding, bracket matching,
indentation guides that highlight the block you are in, a real find-and-replace
panel with a match count, and
per-language grammars fetched on demand so opening a .tsx doesn't pay for
Rust and Python. Word completion from the open document covers the "finish this
identifier" case without any of the machinery below.
Language servers are there when you want them, and off until you do. Switch
them on in the editor's settings and FigyTerm speaks LSP to the servers already
installed on your machine — nothing is bundled and nothing is downloaded, the
same reasoning that has it shell out to git. That gets you real diagnostics in
the gutter, hover types, completion that knows what foo is, signature help,
go-to-definition and find-references, rename across files, formatting, and quick
fixes on the error under the cursor.
It stays off by default on purpose: a language server is a heavyweight process —
rust-analyzer on a large repository is measured in gigabytes — and this is a
terminal that starts fast. One starts on the first file of its language, stops
when idle, restarts if it crashes (three times, then it stops and says so), and
is reaped when the app quits. The status bar shows which server is attached and
what it is doing, because "rust-analyzer: indexing" is the difference between an
editor that is slow and one that looks broken.
Twenty-four servers are known out of the box, and the settings panel lists
every one with whether it is on your PATH and the single command that installs
it if not: TypeScript/JavaScript, Rust, Python, Go, C/C++/Objective-C, PHP,
Ruby, Swift, Zig, Dart, Kotlin, C#, Svelte, Terraform, TOML, Markdown, Dockerfile,
JSON, CSS/SCSS/Less, HTML, YAML, Shell, Lua — and Tailwind CSS, which runs
alongside whichever server already owns the file rather than replacing it, so
class-name completion in a .tsx costs you nothing. Each can be switched off on
its own, or pointed at your own build.
Servers needing bespoke bootstrapping are deliberately absent — jdtls wants a
per-workspace data directory, and Roslyn's server wants the solution opened
through a notification that isn't in the specification — because those need
handling, not a table row. See the design notes and
what was built.
Saving is the part that had to be right. Writes go to a sibling temp file and are renamed over the target, so a crash can't leave a half-written file. Every save carries the mtime the buffer was loaded at, so a file changed by something else is never silently clobbered — you get overwrite, reload, or cancel. Line endings and byte-order marks are written back exactly as found, which is what stops a one-line edit becoming a whole-file diff. Dirty buffers are journalled while you type and offered back after a crash.
It knows it's in a terminal. Paths in output are clickable — a tsc error,
a stack trace, a grep -n hit — and open at the right line. The tree offers
"open a terminal here". The workspace follows the shell.
Git is built in. Changed files are coloured and badged in the tree, changed
lines get a bar in the gutter beside the line number, and the branch (with
ahead/behind) sits in the status bar. The changes panel is shaped like GitHub
Desktop's: one list of what changed, a checkbox per file for what goes in the
next commit, and a summary/description box with Commit N files to main.
Clicking a file opens its diff against HEAD in a diff-check tab beside your
files, unified or split, with the changed words inside a line highlighted
the way GitHub does it — and a style picker covering GitHub, GitLab, VS Code,
delta and plain git diff, so it can look like whichever one you already read
diffs in. It shells out to your own git, so
your hooks, credential helper and .gitattributes all apply. Discarding an
untracked file moves it to the trash rather than deleting it, which is the one
thing git clean gets wrong for an editor. A History tab lists your commits, and
opening one slides in a drawer with its message — folded behind Read more
when it's long — and the files it touched, counted by what happened to them
("24 edited · 6 new · 1 deleted"). Click a file to diff it at that revision, or
Back to return to the list. A commit's SHA and the branch name are links — they
open on GitHub, GitLab or Bitbucket, worked out from your remote.
Fetch and Push are there — push publishes a branch that
has no upstream yet. Hunk-level staging, branch switching and pull are
deliberately left to the shell: a pull can leave a conflict, and a merge editor
is a feature of its own.
Workspaces each remember their own open tabs and expanded folders, can be starred, and are switched from a picker (click the folder name in the breadcrumb). Both the editor and the browser can go picture-in-picture or fullscreen, and clicking one brings it to the front.
Deliberately absent for now: git decorations in the tree, diffing, and a language server. Reasons and a plan are in docs/CODE-EDITOR.md.
FigyTerm ships with built-in specs for popular tools:
| Command | Coverage |
|---|---|
git |
Subcommands, branches, options |
docker |
Commands, containers, images, options |
docker compose |
Services, commands, options |
npm |
Scripts, packages, options |
pnpm |
Scripts, workspaces, options |
yarn |
Scripts, packages, options |
uv |
Scripts from pyproject.toml, options |
cd |
Directories with recent-first ordering |
Adding a new spec is straightforward — see the Spec Authoring Guide.
┌─────────────────────────────────────────────┐
│ React + TypeScript (UI Layer) │
│ ├── xterm.js (terminal rendering) │
│ ├── Autocomplete engine (spec-based) │
│ ├── Split panes (react-resizable-panels) │
│ ├── Browser modal (native child webview) │
│ ├── Code editor (CodeMirror 6, in-webview)│
│ └── Settings & theme management │
├─────────────────────────────────────────────┤
│ Tauri IPC (commands + events) │
├─────────────────────────────────────────────┤
│ Rust (Native Layer) │
│ ├── PTY session management │
│ ├── Path completion (filesystem) │
│ ├── Editor filesystem (confined to roots) │
│ ├── File watching (notify) & project grep │
│ ├── Shell command execution │
│ ├── Browser webviews (multi-webview) │
│ └── Window management │
└─────────────────────────────────────────────┘
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Vite, Tailwind CSS |
| Terminal | xterm.js, FitAddon, WebLinksAddon |
| Editor | CodeMirror 6 (per-language grammars, lazily imported) |
| Desktop | Tauri 2.x |
| Native | Rust |
| PTY | portable-pty |
| File watching | notify |
| Project search | ignore (ripgrep's walker), regex |
| State | Zustand |
| UI Components | Headless UI, Lucide Icons |
| Panels | react-resizable-panels |
# Install dependencies
npm install
# Start dev mode (hot-reload frontend + Rust rebuild)
npm run tauri dev
# Type check
npx tsc --noEmit
# Production build
npm run tauri buildfigyterm/
├── src/ # Frontend (React + TypeScript)
│ ├── components/
│ │ ├── AppShell/ # Main layout, tab & pane management
│ │ ├── Browser/ # Embedded browser modal
│ │ ├── Editor/ # Code editor: surface, tabs, tree, preview
│ │ │ └── lsp/ # CodeMirror ⇄ LSP: diagnostics, hover, actions
│ │ ├── Overlay/ # Overlay portal & error boundary
│ │ ├── Terminal/ # Terminal, TabBar, HistorySearch, SystemMonitor
│ │ └── Settings/ # Settings modal (tabbed)
│ ├── hooks/ # Draggable modals, file watching
│ ├── services/ # Autocomplete, browser & editor IPC, specs
│ │ └── lsp/ # Language-server client, lifecycle, positions
│ ├── specs/ # Command completion specs (git, docker, etc.)
│ ├── stores/ # Zustand stores (settings, theme, editor)
│ └── types/ # TypeScript definitions (figy, terminal)
├── src-tauri/ # Backend (Rust)
│ └── src/
│ ├── commands/ # Tauri IPC handlers
│ │ ├── terminal.rs # PTY session create/write/resize/close
│ │ ├── browser.rs # Child webview lifecycle & navigation
│ │ ├── autocomplete.rs # Path completions & shell history
│ │ ├── fs.rs # Editor filesystem, confined to roots
│ │ ├── fs_watch.rs # File watching, debounced
│ │ ├── lsp.rs # Language-server start/send/stop/status
│ │ ├── system.rs # CPU/memory stats
│ │ └── shell_exec.rs # Shell command execution
│ ├── filesystem/ # Read, atomic write, search, hidden files
│ ├── lsp/ # Server processes & Content-Length framing
│ ├── spawn.rs # Finding the user's tools: PATH, PATHEXT
│ └── lib.rs # App entry point
├── docs/ # Documentation
│ ├── CODE-EDITOR.md # Editor design notes & decisions
│ ├── LSP.md # Language server design & rationale
│ ├── LSP-TASKS.md # What was built, and what is still unproven
│ ├── CONTRIBUTING.md # Contribution guidelines
│ └── SPECS.md # Spec authoring guide
├── public/ # Static assets (logo, icons)
└── package.json
- Real PTY sessions with persistent shell
- Multi-tab support with rename
- Intelligent autocomplete (spec-based)
- Split panes (up to 4 per tab)
- Oh My Zsh theme management
- Dark/Light mode
- Recent directory ordering
- Clickable URLs
- Command history search (
⌘R) - Terminal search (
⌘F) - System monitor (
⌘⇧M) - Embedded browser with tabs (
⌘⇧B) - Embedded code editor with file tree (
⌘⇧E) - Markdown preview with a synced outline
- Language server support in the editor, opt-in per language
- Git status in the editor's file tree and gutter
- Plugin system for custom specs
- AI-powered command suggestions (local models)
- Snippet management
- Session restore on relaunch
We welcome contributions! Please read our Contributing Guide to get started.

