Blazing-fast, intelligent terminal autocomplete daemon for Fish and Bash.
Archaic is a C-based autocomplete daemon that indexes your filesystem and serves context-aware, frequency-weighted path completions in sub-milliseconds. It runs as a background process, learns from your usage patterns, and integrates seamlessly with Fish and Bash shells.
- Sub-millisecond query latency — radix tree + binary search + LRU cache
- Intelligent scoring — frequency, recency, depth, and type weighted ranking
- Enhanced fuzzy matching — 5-strategy scoring: exact prefix, segment tokenization (acronym matching), path-boundary bonuses, Levenshtein typo tolerance, and full-path fallback
- Background filesystem scanning — multi-threaded, configurable depth
- Real-time filesystem watching — inotify (Linux) / kqueue (macOS) with polling fallback
- Periodic auto-rescan — stays in sync with filesystem changes
- State persistence — survives daemon restarts via binary save/load (atomic write)
- TOML configuration — sensible defaults, fully customizable
- Fish, Bash, and Zsh integration — tab completion and inline suggestions
- Neovim integration — nvim-cmp source, Telescope picker, user commands
- Unix domain socket IPC — versioned binary protocol for client communication
- Memory safety — configurable limits, per-bucket refcounting, LRU eviction
- Zero external dependencies — pure C23, POSIX, pthreads (except libfmt for logging)
Fish/Bash/Zsh → archaic-helper (persistent) → Unix Socket → archaic-daemon
├── Radix Tree (sorted, bsearch)
├── Bucket Store (65536, LRU)
├── Query Cache (LRU + TTL)
├── Enhanced Fuzzy (5-strategy)
├── Filesystem Watcher (inotify/kqueue)
└── Parallel Scanner (4 threads)
The daemon runs as a persistent background process. Fish communicates through
archaic-helper, which maintains a persistent Unix domain socket connection to
avoid per-invocation overhead. The daemon holds a radix tree of indexed paths,
a bucket store for frequency tracking, and an LRU query cache for repeated
lookups.
git clone https://github.com/Samoreilly/Archaic.git
cd Archaic
./install.sh ~/projectsThis builds the project, starts the daemon, and installs shell integration for your current shell (Fish, Bash, or Zsh) — no further steps required.
brew install --formula Formula/archaic.rb
archaic --daemon ~/projectsnix build
./result/bin/archaic --daemon ~/projects# 1. Build
git clone https://github.com/Samoreilly/Archaic.git
cd Archaic
mkdir build && cd build
cmake .. && make
# 2. Start the daemon
./run.sh start ~/projects
# 3. Install shell integration
./run.sh install-fish # Fish shell
./run.sh install-bash # Bash
./run.sh install-zsh # Zsh
# 4. Restart your shell or run:
# Fish: source ~/.config/fish/conf.d/archaic.fish
# Bash: source ~/.local/share/bash-completion/completions/archaic.bash
# Zsh: source ~/.config/zsh/archaic.zshArchaic reads configuration from the first path found in this order:
$ARCHAIC_CONFIG(environment variable)~/.config/archaic/config.toml/etc/archaic/config.toml
[daemon]
scan_path = "/home/user/projects"
socket_path = "/tmp/archaic-daemon.sock"
scan_threads = 4
max_depth = 10
rescan_interval_seconds = 300
[storage]
max_buckets = 65536
max_nodes_per_bucket = 100000
cache_max_entries = 1024
cache_ttl_seconds = 2
[scoring]
weight_frequency = 0.40
weight_recency = 0.30
weight_depth = 0.20
weight_type = 0.10
[fish]
commands = ["cd", "ls", "cat", "vim", "nvim", "less", "bat", "rm", "mv", "cp", "mkdir", "touch"]
[scanner]
ignore_dirs = [".git", "node_modules", "build", "dist", "__pycache__", ".venv"]
ignore_files = ["*.pyc", "*.o", "*.log", ".DS_Store"]| Section | Key | Default | Description |
|---|---|---|---|
daemon |
scan_path |
(required) | Root directory to index |
daemon |
socket_path |
/tmp/archaic-daemon.sock |
Unix socket location |
daemon |
scan_threads |
4 |
Parallel scanner thread count |
daemon |
max_depth |
10 |
Maximum directory traversal depth |
daemon |
rescan_interval_seconds |
300 |
Auto-rescan interval (0 to disable) |
daemon |
watcher_fallback_interval |
30 |
Polling interval when inotify/kqueue unavailable |
storage |
max_buckets |
65536 |
Hash bucket count for path store |
storage |
max_nodes_per_bucket |
100000 |
Max entries per bucket |
storage |
cache_max_entries |
1024 |
Query cache size |
storage |
cache_ttl_seconds |
2 |
Query cache time-to-live |
scoring |
weight_frequency |
0.40 |
Usage frequency weight |
scoring |
weight_recency |
0.30 |
Last-access recency weight |
scoring |
weight_depth |
0.20 |
Path depth preference weight |
scoring |
weight_type |
0.10 |
File type preference weight |
fish |
commands |
(see above) | Commands that trigger completions (Fish) |
bash |
commands |
(see above) | Commands that trigger completions (Bash) |
scanner |
ignore_dirs |
(see above) | Directories to skip during scan |
scanner |
ignore_files |
(see above) | File patterns to skip during scan |
./run.sh start [path] # Start daemon scanning path
./run.sh stop # Stop daemon
./run.sh restart [path] # Restart daemon
./run.sh status # Check daemon status
./run.sh rescan # Trigger manual rescan
./run.sh install-fish # Install Fish shell plugin
./run.sh uninstall-fish # Remove Fish shell plugin
./run.sh install-bash # Install Bash completion script
./run.sh uninstall-bash # Remove Bash completion script
./run.sh install-zsh # Install Zsh completion script
./run.sh uninstall-zsh # Remove Zsh completion scriptarchaic-cli scan <path> # Trigger filesystem scan
archaic-cli query <cwd> <input> # Validate path
archaic-cli complete <prefix> [n] # Get completions
archaic-cli suggest <prefix> # Get best suggestion
archaic-cli ping # Check daemon health
archaic-cli metrics # View daemon metrics
archaic-cli scan-status # Check scan progress
archaic-cli save <path> # Save state to file
archaic-cli shutdown # Stop daemonarchaic-helper [socket_path] # Start persistent helper
# Then pipe commands: echo "complete /path 20" | archaic-helperThe helper maintains a persistent connection to the daemon, eliminating the overhead of socket setup for each query. This is what the Fish plugin uses internally.
Archaic provides two completion modes in Fish:
- Tab completion — press
Tabto cycle through ranked path suggestions - Inline suggestions — ghost text appears as a right-prompt suggestion
| Key | Action |
|---|---|
Tab |
Cycle through completions |
Alt+Right |
Accept inline suggestion |
Ctrl+R |
Accept inline suggestion |
Archaic provides tab completion for Bash:
- Tab completion — press
Tabto cycle through ranked path suggestions - Directory-only filtering for
cd,mkdir,pushd,popd,rmdir - Fuzzy fallback when exact prefix match returns no results
./run.sh install-bash
# Or manually:
source ~/.local/share/bash-completion/completions/archaic.bashArchaic provides tab completion for Zsh with the same features as Bash.
# Add to ~/.zshrc:
source /path/to/archaic/zsh/archaic.zshArchaic provides integration with Neovim through two modes:
- nvim-cmp source — autocomplete paths while typing
- Telescope picker — fuzzy-find files with
:ArchaicFind
{
"Samoreilly/Archaic",
lazy = true,
dir = "/path/to/archaic/nvim",
config = function()
require("archaic").setup({
socket_path = "/tmp/archaic-daemon.sock",
timeout_ms = 100,
enable_cmp = true,
enable_telescope = true,
})
end,
}-- Add to your init.lua or a plugin spec:
local archaic = require("archaic")
archaic.setup({})
-- User commands:
-- :ArchaicStatus — check daemon status
-- :ArchaicReindex — trigger rescan
-- :ArchaicToggle — toggle completions
-- :ArchaicCheckHealth — health check
-- Telescope extension:
require("telescope").load_extension("archaic")
:Telescope archaic find_files- Commands listed in
[fish].commandsor[bash].commandstrigger completions cdandmkdirfilter to directories only- The plugin monitors daemon health and falls back gracefully if unavailable
- Suggestions are ranked by the daemon's scoring algorithm
Archaic's speed comes from several layered optimizations:
- Path-compressed trie (radix tree) — common path prefixes are stored once, reducing memory and traversal time
- Binary search on sorted children — child node lookups use
bsearchfor O(log n) instead of linear scan - O(log n) duplicate detection — scored completions use binary search to avoid duplicates without hashing overhead
- LRU cache with TTL — repeated queries hit the cache instead of traversing the tree
- Per-bucket refcounting — lock-free concurrent reads via atomic reference counts on each hash bucket
- 4-thread parallel scanner — filesystem indexing runs across multiple threads with work-stealing
- Real-time filesystem watching — inotify/kqueue push updates instead of periodic full rescans
Archaic uses a 5-strategy scoring system for fuzzy path matching:
| Strategy | Priority | Example |
|---|---|---|
| Exact prefix | 1000+ | src/ma → src/main.c |
| Segment tokenization | 100-500 | srma → src/main.c, sio → src/io/ |
| Subsequence + path bonuses | 50-200 | srcmai → src/main.c |
| Levenshtein basename | 10-49 | maim → main.c (1 edit) |
| Levenshtein full path | 1-9 | src/maim → src/main.c |
Segment tokenization breaks paths on /, ., -, and _ to match
acronyms: typing sio finds src/io/, typing mvcm finds models/view_controller.cr.
Archaic watches for filesystem changes in real time:
- Linux: Uses
inotifyfor immediate event notification - macOS: Uses
kqueuewithEVFILT_VNODEfor directory monitoring - Fallback: Polling-based timer (configurable interval)
Watched directories are added recursively, skipping .git, node_modules,
.venv, __pycache__, build, and dist. New directories are automatically
added when created. Removed directories are cleaned up.
To force a full rescan regardless of the watcher, send SIGHUP or use
archaic-cli scan <path>.
- Check that the socket path is writable:
ls -la /tmp/archaic-daemon.sock - Remove stale socket files:
rm -f /tmp/archaic-daemon.sock - Verify the scan path exists and is readable
- Confirm the daemon is running:
./run.sh status - Verify the scan path is correct:
archaic-cli scan-status - Trigger a manual rescan:
./run.sh rescan - Check that paths exist under the configured
scan_path
- Fish: source the plugin manually:
source ~/.config/fish/conf.d/archaic.fish - Bash: source the script manually:
source ~/.local/share/bash-completion/completions/archaic.bash - Verify the helper is installed and in your
PATH - Check that commands in
[fish].commandsor[bash].commandsmatch your usage
- Reduce
max_bucketsin the[storage]section - Lower
max_nodes_per_bucketto cap per-bucket growth - Decrease
max_depthin[daemon]to limit indexed directories - Restart the daemon to apply configuration changes
MIT License — see LICENSE for details.