pnezis

pnezis

Hi all,

I’m releasing probex, a small dependency-free CLI built for one purpose: coding agents working on Elixir burn a meaningful share of their tokens — and their wall-clock — just re-reading code by guesswork. probex removes that tax. The same task completes in fewer calls, with smaller payloads, and without the re-reads.

Where the tokens actually go

Watch an agent work on a large Elixir codebase and a pattern repeats all day:

  1. grep -n to find where a function starts — grep can’t say where it ends;
  2. guess a window: sed -n '148,200p';
  3. the window clips the block (or drags in three neighbours), so read again, wider;
  4. repeat per function, per file, per task.

Every guessed window is paid for in tokens whether or not it was the right window, every miss is paid for twice, and every one of those calls is a full agent round-trip. Mining one week of our agent transcripts found 2,369 windowed reads with a median window of 54 lines — for answers that are usually a dozen lines, exactly bounded, and known to the parser all along.

probex asks the parser instead:

$ probex body lib/accounts.ex register_user/2
lib/accounts.ex:148-163  def register_user/2   [defmodule MyApp.Accounts]
  def register_user(attrs, opts \\ []) do
    ...
  end

Exact boundaries, so a heredoc containing end, a sigil containing a fake def, or a keyword-form body can’t produce a clipped read. And one call takes many files × many selectors, so what used to be a shell loop of N reads is one round-trip:

probex body lib/a.ex lib/b.ex changeset/2 valid?/1   # both blocks, in both files
probex body lib/ handle_info/2                       # every match under a directory
probex outline big_test.exs --kind describe,test     # exact TOC, no bodies
probex cover my_app --fun register_user/2 --body     # did my new test go green?

The same economics drove every command: outline replaces “read the first 200 lines to orient”; directives replaces the header-window read; cover replaces the most reinvented wheel in our transcripts (agents writing one-off coverage-HTML parsers) with uncovered lines attributed to the functions that contain them, reconciling with mix test.coverage including :ignore_modules. Errors are budgeted too: a miss returns the corrected command or the list of what does exist, so the next call is the answer instead of another probe.

Reading commands parse, never compile — no mix, no deps, no app boot — so they work mid-refactor on files that don’t currently build.

How it was built (the part I find fun)

No feature came from a human wishlist. It started with me being annoyed at the token bill; the ask was one sentence — this is wasteful, improve it. Every decision after that was the agents’:

  • The feature set was mined from the agents’ own shell transcripts — the most repeated waste became the founding commands.
  • Nothing ships without measured demand: invocations are logged (opt-in, PROBEX_LOGFILE), probex stats reports which commands and options get used and which never do, and an end-of-day skill mined each day’s sessions for gaps and proposed extensions. An early intuition-ranked roadmap got its top pick wrong; evidence has decided ever since.
  • Agents improve it for agents: feedback filed by the agents using it, fixes made by agents with the test suite as the north star, and probex prompt --write installs the instructions into AGENTS.md / CLAUDE.md from the binary itself — a test fails if a flag exists that the prompt doesn’t teach.

My role: ask once, run the loop, and pay for the tokens.

Install

git clone https://github.com/pnezis/probex.git
ln -s "$PWD/probex/probex" /usr/local/bin/probex
probex prompt --write     # teach it to your agents

Status: alpha, but not experimental — extracted from an internal tool used daily by a fleet of agents against a production monorepo.

License: MIT

If you run agents against elixir projects, try it and bring back your usage log: the entire project is an argument that agent tooling should be chosen by the agents’ measured usage, and your evidence makes it better. :tada:

Showing Posts 1 to 7

pnezis

pnezis OP

Did you compare the performance against: GitHub - DeusData/codebase-memory-mcp: High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies. · GitHub or other similar tools?

@onnimonni Not benchmarked, because they’re for different steps of the loop. An indexer answers facts about code — where a symbol lives, who calls it, how modules connect — and its token savings come from returning graph facts instead of source. probex serves the step after that: once you know where to look, an agent still has to read the code to change it, and that’s what probex bounds exactly — the body of one function, all clauses of a multi-clause one (one symbol, several disjoint ranges), a file’s outline with real end lines, the test block by title, the uncovered lines grouped by function.

So it’s navigation vs. reading — they compose rather than compete. An indexer telling you accounts.ex:148 is exactly what feeds probex body accounts.ex:148.

If someone runs both in one agent loop I’d genuinely like to hear how it goes.

pnezis

pnezis OP

Have you compared against GitHub - jahala/tilth: Smart(er) code reading for humans and AI agents. Reduces cost per correct answer by ~40% on average. Install: cargo install tilth -or- npx tilth · GitHub? It’s similar, not 100% but pretty close.

I haven’t — honestly, I didn’t know about tilth until this comment, so thanks for the pointer. It does look close, and the benchmark methodology (cost per correct answer) is exactly the right metric for this class of tool.

The difference is a deliberate bet rather than a feature gap: probex is Elixir-only, on the language’s own parser instead of a tree-sitter grammar. A generic grammar sees Elixir’s syntax; the compiler’s parser sees the language — and that’s what gives us the freedom to keep extending along the things that are unique to Elixir rather than common to 16 languages. Multi-clause functions as first-class citizens (one symbol, several disjoint ranges, addressable together or per clause), describe/test blocks by title, macro DSL blocks in routers and schemas, defstruct with its @derive/@enforce_keys run, the preamble of a test file as a nameable thing. And it goes below source level where tree-sitter can’t follow: cover joins mix test --cover data back to the functions that own each line, down to what the BEAM actually compiled — overridable renames, macro-injected functions.

dimitarvp

dimitarvp

Thank you, thought so. tilth first, then probex indeed.

Here’s what Fable has to say about probex for my work:


Feedback from a first day of real use (Elixir 1.20/OTP 29), from an agent loop that already ran tilth for navigation:

  1. Nested defmodule blocks are unreachable through body. Given defmodule Grace do … defmodule CloseFacts do defstruct … end … end: probex body grace.ex defmodule:CloseFacts reports no match — while the tool’s own “no match for CloseFacts” hint suggests exactly that command. probex body grace.ex L58 (a line inside CloseFacts, outside any function) errors “inside defmodule CloseFacts (37-75), not inside any function” instead of returning the enclosing module block. outline lists the nested module with the right range, so the information is there. Struct-per-nested-module is a common Elixir layout (Types.Breakdown, Grace.CloseFacts), so this one hits daily.
  2. Arity-lenient match is silent. probex body coordinator.ex calculate_interest_state/3 returned calculate_interest_state/4 with exit 0 and nothing on stderr. Returning the near miss is useful, but it should say so (or exit 2 with the candidate) — per the README’s own “no silent no-ops” rule.
  3. test:/describe: titles are exact-only. Agents usually know a fragment of a long title; substring (or glob) matching, or listing near titles on a miss, would remove one round-trip.
  4. Single-line module attributes (@golden_sha256 "…") are not addressable (no match, no list of what exists).
  5. Cosmetic: preamble prints range 1-17 but labels it 7 lines.
  6. .eex/.heex give “cannot parse — unexpected token” rather than an “unsupported extension” refusal.

What worked exactly, for the record: name/arity bodies, multi-selector batches, clauses on head-dispatch functions (and the ambiguity error pointing at it), keyword-form one-liners, L<n>/file:N, outline --kind describe,test, preamble, directives, --head, --decorators, and cover against a real mix test --cover --export-coverage run (per-function attribution, correct source). Payload on nine real reads: about 40% of the sed windows they replaced, zero clipped blocks.

pnezis

pnezis OP

@dimitarvp thanks a lot for fable’s feedback on probex, most items are addressed and live on main

dimitarvp

dimitarvp

Thank you. Fable confirmed 5/6 fixed (after running tests on our codebase) and said only the substring/glob matching for test: / describe: titles is left from its original batch of feedback.

probex has been hugely useful to me even for just a few days of usage; a real token reduction. And I don’t always care about token expenditure TBF; but the extra turns that the agents need to find exact code blocks really do take extra time. With probex the amount of turns got reduced (not by much, maybe 3-5%, but having in mind that sometimes a turn can take 5-10 seconds, it does add up and the difference is felt).

So, super thank you. Genuine improvement to the ecosystem.

— All posts loaded —

Where Next? Top

Trending in Announcing Top

forfun
Hi everyone! I’m building Handbeam, an experimental open-source AI agent for mobile devices, built with Elixir and an embedded Erlang/OTP...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. https://github.com/woylie/doggo Features Unstyled Phoenix components....
New
GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews