zigworks
trigrep 0.2.1 and headterm 0.2.0 released

Small, fast tools that coding agents run on, written in Zig.

trigrep searches huge codebases in milliseconds and never misses a file you just saved. headterm lets an agent drive programs that need a real terminal. Both are command-line tools and MCP servers.

$ claude mcp add --scope user trigrep -- trigrep mcp

Download a release View the source

Searching the Linux kernel
trigrep 45 ms
ripgrep 1,432 ms

97,000 files, 1.7 GB, Apple Silicon, median of 5 runs. Bars play back in real time. Output is identical.

trigrep

Instead of grep and ripgrep on big repositories

Indexed code search with ripgrep’s flags and output. A file watcher keeps the index current, so a search always sees the edit you made a second ago.

~/linux
$ trigrep -w spin_lock_irqsave kernel/sched
kernel/sched/core.c:9404:	spin_lock_irqsave(&task_group_lock, flags);
kernel/sched/core.c:9448:	spin_lock_irqsave(&task_group_lock, flags);
kernel/sched/fair.c:4110:		spin_lock_irqsave(&grp->lock, flags);
kernel/sched/wait.c:24:	spin_lock_irqsave(&wq_head->lock, flags);
… 12 lines in 3 files; total 3.6 ms
How trigrep works

headterm

Instead of tmux scripts and expect

A terminal an agent can use. It returns the screen as text, the rows that changed, and which text is highlighted, so menus, editors and installers stop being dead ends.

headterm wait menu --text warning --styles
Install which toolchain?

  stable 0.15.1
  nightly 0.16.0-dev  
  skip for now

1 warning: PATH not set
row 3: [0-22 inverse "  nightly 0.16.0-dev"]
How headterm works

In your agent

Register both as MCP servers and the agent picks them up as tools. This is a real Claude Code session: the prompt is shortened, the tool calls and results are verbatim.

You

Find spin_lock_irqsave in the kernel’s C files, then use Python to work out 2**10.

trigrep_search{"pattern": "spin_lock_irqsave", "glob": ["*.c"], "max_results": 3}
arch/alpha/kernel/irq_i8259.c:41:	raw_spin_lock_irqsave(&i8259_irq_lock, flags);
arch/alpha/kernel/irq_i8259.c:57:	raw_spin_lock_irqsave(&i8259_irq_lock, flags);
arch/alpha/kernel/irq_i8259.c:68:	raw_spin_lock_irqsave(&i8259_irq_lock, flags);
[stopped at 3 lines; narrow the pattern, add a glob or path, or raise max_results]
headterm_start{"name": "cc-test", "command": ["python3", "-q"], "then_wait": {"text": ">>> "}}
started cc-test pid=38879
>>>
[headterm] cc-test 120x40 cursor=0,0 running wait=ok(text) waited=56ms
headterm_send{"name": "cc-test", "keys": ["print(2**10)", "<Enter>"], "then_wait": {"text": "1024"}}
>>> print(2**10)
1024
[headterm] cc-test 120x40 cursor=2,0 running wait=ok(text) waited=4ms
headterm_kill{"name": "cc-test"}
killed cc-test

trigrep

A trigram index narrows every search to the files that can match. On the Linux kernel that turns a 1.4 second scan into tens of milliseconds.

Linux kernel, with trigrep watch running. Each row is one search; bars share one scale.
  1. spin_lock_irqsave45 ms1,432 ms
  2. EXPORT_SYMBOL_GPL\(kmalloc12 ms1,434 ms
  3. zzzqqqxxx (no match)12 ms1,406 ms
  4. ^static int __init \w+_init\(void\)55 ms1,464 ms
  5. TODO|FIXME|XXX160 ms1,388 ms
  6. struct \w+_ops \w+ = \{135 ms1,625 ms

trigrep ripgrep 15.2

Same answers as ripgrep

Every benchmark search is diffed against ripgrep, line by line. The comparison script ships in the repository, so you can run it on your own code.

Patterns with no literal of three or more characters, like \d{4}-\d{2}-\d{2}, can’t use the index. They scan every file at about ripgrep’s speed.

scripts/compare.sh
$ compare.sh linux spin_lock_irqsave -C 2
same  94866 lines  [spin_lock_irqsave] -C 2
$ compare.sh linux 'EXPORT_SYMBOL\w*' -o -t c
same  40792 lines  [EXPORT_SYMBOL\w*] -o -t c
$ compare.sh linux '[α-ω]+'
same  90 lines  [[α-ω]+]
$ compare.sh linux 'TODO|FIXME|XXX'
same  22499 lines  [TODO|FIXME|XXX]

headterm

Agents can run commands but not use them when a program wants a keyboard. headterm puts the program in a pseudo-terminal and gives the agent eyes and hands.

  1. Start a program

    headterm start py -- python3 -q
  2. Send keys

    Text, <Enter>, <C-c>, arrows, mouse clicks and drags.

    headterm send py 'print(2**10)' '<Enter>'
  3. Wait and read the screen

    For text, a regex, quiet output or exit. Then the screen comes back as plain text.

    headterm wait py --text 1024
  4. Close it

    Or let it go: idle sessions close themselves after an hour.

    headterm kill py
what the agent receives
>>> print(2**10)
1024
>>>
[headterm] py 60x6 cursor=2,4 running wait=ok(text) waited=0ms

Press keys the way an agent does

An installer’s menu, as headterm reports it. The selected row comes back as an inverse run, so the agent knows which option it is on before it presses Enter.

An example in headterm’s output format, running in your browser.

headterm screen menu --styles
Install which toolchain?

  stable 0.15.1
  nightly 0.16.0-dev    
  skip for now

row 3: [0-22 inverse "  nightly 0.16.0-dev"]
[headterm] menu 34x8 cursor=3,0 running

Install

Single static binaries for Linux (x86_64, arm64) and macOS (Apple Silicon). No runtime, nothing else to install.

Download a release

With the binaries on your PATH:

claude mcp add --scope user trigrep -- trigrep mcp
claude mcp add --scope user headterm -- headterm mcp
Dependencies
The Zig standard library and libc.
Size
500 to 700 KB per binary.
Testing
Linux and macOS, on every commit.
License
MIT, on GitHub.