Out of the box, coding agents (Claude Code, Codex, Cursor, Antigravity)
routinely fall into Julia-specific traps: measuring @allocated at top level
(where it measures global variable boxing instead of the function), defining
overlapping parametric methods that fail precompilation, or leaving struct
fields untyped or abstractly typed.
I’ve packaged the directives, structural linters, and developer tooling I use
to prevent those pitfalls into one repo:
GitHub - durantschoon/julia-agent-tools · GitHub (MIT).
The primary workflow is installing the directives into your Julia project
(./bin/julia-agent-tools install-directives /path/to/repo), which gives your
agent both the rules and the local gates to check its own edits:
- Agent directives (
directives/): Drop-inCLAUDE.md,AGENTS.md,
.cursorrules, and.agents/rules/configurations encoding type stability,
zero-allocation hot paths, precompilation hygiene, and how to navigate and
test a Julia repo.docs/lessons.mddocuments the twelve real-world Julia
failure modes behind the rules. - ast-grep support for Julia (
rules/): A tree-sitter build plus a
catalogue of structural search and linting rules that catch anti-patterns
(untyped/abstract struct fields, top-level@allocated, overlapping
supertype methods, unchecked type parameters) in milliseconds. JuliaAgentTools.jl& CLI: Wraps the same checks for the REPL, CI, or
agent verification loops (audit_allocations,is_type_stable, AST
scanning, tag generation).- Universal Ctags optlib (
ctags.d/julia.ctags): Token-free symbol
navigation for abstract types, structs (mutable and immutable as separate
kinds), methods, macros, modules, and type aliases (make install-ctags). - Works with graphify: Pairs
with graphify (which builds AST knowledge graphs from the same upstream
tree-sitter-juliagrammar this repo vendors) so agents query call graphs
and type hierarchies instead of blind grepping.
All of the structural search, linting, ctags, and audit tools work standalone
from the CLI, editor, or REPL without an LLM in the loop—for example,
./bin/julia-agent-tools run -k where_expression src/ lists every parametric
method in a tree.
What changed for me, measured rather than felt: I gave a headless agent
the same ten small tasks (a new struct, a hot loop, a parametric type, an
allocation test, a threaded sum, …) on a bare copy of a package of mine
and on a copy with the directives installed, each task from a clean
checkout, scored by these rules and the package’s own tests, three times over.
Bare: 3, 2 and 1 of 10 tasks introduced a flagged mistake, every one of
them @allocated at top level in a test, copied from the package’s own
house style, plus one test suite the bare agent broke. Tooled: 1, 0 and 0
of 10; the one slip was a parametric struct without an inner constructor,
which the new rule caught, and no broken suites. Three runs is a
direction with a range, not a precise number; the tasks, scores and
script are in docs/ab-measurement.md so you can run it on your own
package. The adversarial testing of the rules themselves is in
docs/ADVERSARIAL_TESTING.md.
What it does not do: It is not a formatter, not a replacement for
JET.jl or Aqua.jl (the audits are coarser and faster, meant as a ~10ms gate an
agent runs on every edit), and the ast-grep rules cover common pitfalls rather
than the whole language.
Install: install ast-grep via Cargo (cargo install ast-grep --locked), uv/pipx (uv tool install ast-grep-cli), npm (npm i -g @ast-grep/cli), Homebrew (brew install ast-grep), or your system package manager, then run make dylib and make test-ast in the repo. Linux and macOS are tested in CI.
If you try it, the most useful thing you can tell me is a Julia idiom the
rules get wrong. Issues welcome; so are rule contributions—each rule is a
small YAML file with its own test.