laya-codex
Health Uyari
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Gecti
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Ranked code retrieval for Claude Code: tree-sitter + BM25 + the Laya re-ranker, delivered through hooks and MCP
laya-codex
Claude Code gets the right code with your prompt: 34% fewer turns, 41% fewer tool calls, 12%
less time, and the right files named more often.
laya-codex indexes your repository on your machine and, before Claude starts each task, gives it the
code that task needs. Claude skips most of the grep-and-open-files hunt.
Install
macOS (Apple Silicon) or Linux x86_64. Installation takes about a minute, plus the model download on macOS.
1. Install the laya-codex binary
curl -fsSL https://raw.githubusercontent.com/pilotspace/laya-codex/main/install.sh | sh
2. Turn it on in Claude Code, for every repository at once:
/plugin marketplace add pilotspace/laya-codex
/plugin install laya-codex@laya-codex
That's it. Open Claude Code in any git repository: laya-codex indexes it in the background and starts
helping from the next prompt. To check the setup, run laya-codex doctor --repo ..
- One repository only, without the plugin:
laya-codex init --repo /path/to/repowrites laya-codex's hooks into
that repository's.claude/settings.local.jsonand its MCP server into.mcp.json. - Share with your team: choose project scope when you run
/plugin install. That records
the plugin in.claude/settings.json. - Homebrew: installs the same prebuilt binaries. The model is a separate step:
brew install pilotspace/tap/laya-codex curl -fsSL https://raw.githubusercontent.com/pilotspace/laya-codex/main/install.sh | sh -s -- --model-only - Installer options:
--model-onlyfetches and verifies only the model and leaves the binaries alone; use it afterbrew install;--version vX.Y.Zinstalls a specific release;--dir DIRinstalls somewhere other than~/.local/bin;--no-modelskips the ~850 MB model, and laya-codex ranks by keywords alone;--modeldownloads the model on Linux too, where it runs on the CPU and is slow.
- Build from source: see Building from source.
What changes for Claude: one example
You ask Claude:
Bug:
initwrites through a symlinked.claudefolder into another repo. Find the check and fix it.
| Stock Claude Code | With laya-codex | |
|---|---|---|
| What Claude has before its first turn | Only your prompt | Your prompt, plus the function that does the check (check_target), its code, and the line that calls it |
| What Claude does first | Searches with grep and opens files until it finds the check | Reads the check it was given and starts fixing it |
| Turn at which a correct file is in context (benchmark v2 median, 60 tasks) | 5.5 | 0 (46 of 60 tasks) |
What laya-codex added to the prompt, trimmed from real output on this repository:
Ranked locations:
1. crates/laya-cli/src/init.rs — 394-425 fn apply; 285-325 fn check_target; …
### crates/laya-cli/src/init.rs:394-425 — fn apply
```rust
pub fn apply(p: &Planned) -> anyhow::Result<()> { …
```
Definitions and uses:
- crates/laya-cli/src/init.rs:288: fn check_target(root: &Path, root_canon: &Path, path: &Path) … — definition of `check_target`
- crates/laya-cli/src/init.rs:472: check_target(root, &root_canon, path)?; — use of `check_target`
Across the whole benchmark, stock Claude Code reached a correct file at turn 4 at the earliest.
laya-codex had one in context before Claude's first turn in most tasks:
More examples in docs/use-cases.md:
- a follow-up in the same session that doesn't resend code;
- impact analysis before changing a signature;
- a first look at an unfamiliar codebase;
- large files, subagents and teams;
- where laya-codex helps less.
Why you might want it
- Claude starts with the right code. The code a task needs arrives with the prompt, so Claude
skips most of the grep-and-open-files hunt: 34% fewer turns, 41% fewer tool calls (Greps fall
from 5.3 to 2.0 per session), 12% less time. - Smaller sessions, lower bills. 22% fewer input tokens and 20% fewer output tokens, so the context
window fills more slowly, and a task costs 9% less. - Better first answers. On the first question of each task, Claude named the right files more
often (answer recall 0.76 → 0.90). - Nothing to learn. It runs through Claude Code's own hooks. You keep prompting as usual.
- Everything stays on your machine. Indexing and ranking run locally, with no server, account or
telemetry. Only the code laya-codex adds to a prompt goes to Anthropic, the same way code Claude
reads with its own tools does. - It can't break Claude Code. Every hook fails open: if laya-codex is missing, stopped or
slow, Claude Code carries on exactly as it would without it.
Benchmark
We compared stock Claude Code with Claude Code plus laya-codex on 60 real code-change tasks from
three open-source repositories: moon (Rust),
httpx (Python) and hono
(TypeScript), none of them used in training. Each task comes from a commit in the repository's
history, and each session asks two questions: where the change goes, then which tests cover it.
Both arms use the same model (Claude Sonnet, medium effort), and all numbers are paired, with 95%
bootstrap confidence intervals. This is benchmark v10 (2026-09-27): laya-codex with the retrained
laya-code-r1 model, 51 tasks (moon stopped at its cost cap after 15 tasks, and 4 of those were
dropped because a Claude API session hung for up to 4 hours in every arm).
| vs stock Claude Code | change | 95% CI |
|---|---|---|
| Turns | −33.6% | −40.4% … −26.5% |
| Tool calls per session | 9.0 → 5.3 | Grep 5.3 → 2.0; laya-codex search 1.0 |
| Wall-clock time | −12.0% | −19.1% … −4.4% |
| Output tokens | −19.8% | −26.8% … −12.3% |
| Total input tokens | −21.7% | −35.1% … −6.4% |
| Cost | −8.8% | −16.3% … −1.9% |
| Code-reading tokens | −32.0% | −47.0% … −15.0% |
| Code read + code injected | +6.2% | −11.3% … +26.3%, not significant |
| Answer recall, first question | 0.90 vs 0.76 | difference +0.07 … +0.22 |
| Answer recall, both questions | 0.94 vs 0.95 | difference −0.07 … +0.03, not significant |
Code reading fell on every repository (−26% on moon, −38% on httpx, −38% on hono), but on httpx
and hono the injected code costs more than that saves.
What limits this result, and what is next
| limit | why | next |
|---|---|---|
| Tokens: code read + injected +6.2%, not significant; our −50% goal is not met | The injection (about 1.7k tokens per session) is as large as the reading it saves | Inject less: fewer and shorter blocks when the model is confident. In progress, gated by the offline replay |
| Time: −12.0%; our −30% goal is not met | Claude now answers lookups with laya-codex search (1.0 per session) instead of Grep, but total tool calls stayed at about 5 per session, and time follows the tool calls and the output |
Make one call answer more, so sessions need fewer of them |
| Model vs keywords: no end-to-end difference | laya-code-r1 inlines more right files offline (70 of 115 vs 62 for keywords), but at 51 tasks the sessions show no significant difference (time −4%, tokens −2%) | A larger run once the injection shrinks |
| Scope: tasks that find and explain code, not edits; one model (Sonnet) | — | Still open: an edit-task pilot |
The report behind these, with every number and the plan for the next run:
docs/RESULTS.md.
Full method, per-repository results, the model-vs-keywords comparison and raw data:
docs/RESULTS.md. To regenerate the charts:
cd scripts
python3 charts.py ../bench/results/headline-v10.json ../docs/assets
python3 insight_charts.py ../bench/results/claude-v8 ../bench/results/replay-2026-09-24 ../docs/assets
How it works
your prompt ─► laya-codex hook ─► local daemon ─► BM25 keyword search + symbol and path matches
│ (Moon index, tree-sitter chunks of 10–50 lines)
├─► Laya re-ranker: "is this code relevant to this task?"
▼
≤ 9,500 chars added to the prompt: a ranked map, the code of the top 2 files
(checked against disk), definitions and uses of the names in your prompt, and
related callers and callees
- Indexing. laya-codex splits your code into 10–50-line chunks along function and class boundaries using
tree-sitter, for 14 languages. Moon, a small local search server that
laya-codex runs for you, stores the chunks. Edits are re-indexed as Claude makes them. - Ranking. Keyword search picks the 24 best candidates, and laya-codex re-ranks the top 16 with the
Laya model:
laya-code, a code-tuned Laya fine-tune running on the
Metal GPU, scores how relevant each one is to your task, and the two rankings are blended. The
model scores the candidates best-first and stops inside its time budget, so a slow or busy
machine re-ranks fewer of them rather than none; the rest keep their keyword order below. Only
if the model is absent, or scores nothing in time, does keyword ranking stand alone. - Adding code without repeating it. laya-codex remembers what the session has already seen, so a
follow-up prompt doesn't get the same code twice, nor code from a file Claude already read whole.
Claude's own Reads are never changed. - Lookups without Grep. Claude also gets an MCP tool,
search(serverlaya-codex). Given a
name (ora|b, optionally with apath), it returns every line that uses it, grouped by
function or test, with the definition marked and a note saying whether the list is complete. So
one call answers "where is it defined, who calls it, which tests cover it". Given a description in
words, it returns ranked code.
What gets injected, when and why, with real hook input and output:
docs/how-it-works.md. Design and decisions:
docs/architecture.md.
Why a Laya model on top of keyword search?
Keyword search is fast and finds the right neighbourhood, but it ranks by shared words. A
function that mentions password five times outranks the one that actually decides whether a
server is trusted. Picking the right piece of code needs a judgment about the task.
- It judges relevance directly. Laya is a
decision model: it reads your task and one piece of code together and answers "is this code
relevant to this task?" with a probability. That is a cross-encoder, which reads both texts
at once. It is more precise than embedding search, where the task and the code are turned
into vectors separately and only their similarity is compared. - It stays cheap. A cross-encoder is too slow to run over a whole repository, so laya-codex
uses it only where it counts. Keyword search narrows the repository to 24 candidates, and the
model scores the top 16 of those, in about 0.5 s on the Metal GPU. Indexing needs no model and no
vector database, so a repository indexes in seconds (Moon's source: 485 files in about 1.2 s). - It has to be tuned for code. Laya was trained for triage, moderation and routing, not code,
and out of the box it ranks code no better than keywords. laya-code is Laya fine-tuned on the
git history of 8 open-source repositories, where each commit's changed files are the right
answers for its message. The two repositories used for evaluation were excluded from training. - The two rankings are blended, not replaced. The final score is
0.5 × keyword rank + 0.5 × model probability. The keyword rank keeps documentation and prose
from crowding out code, and the model reorders the code candidates.
How well each stage ranks the files a real change touched, over the 40 most recent Moon commits
(24 keyword candidates per task, model card):
| ranking | MRR (higher is better) | share of the top 10 that is right (P@10) | calibration error (lower is better) |
|---|---|---|---|
| keyword search (BM25) alone | 0.480 | 0.340 | – |
| base Laya, not tuned for code | 0.479 | 0.348 | 0.362 |
| laya-code | 0.702 | 0.405 | 0.049 |
On a separate development set, the full blended pipeline reached an MRR of 0.724, against
0.602 for the model alone and 0.678 with the model weighted more heavily.
What the end-to-end benchmark shows so far. Benchmark v2 included a keyword-only arm: the
same hooks with the model switched off.
- Over 60 tasks, the model did not put more correct files in front of Claude than keyword ranking
alone (73 vs 76 files inlined). - Sessions with the model ran 13% longer (95% CI +2% … +27%). Its own scoring accounts for about
1 s per prompt, around 4% of a session; the rest came from Claude taking more turns. - The effect changes sign by repository (hono was 10% faster with the model) and between earlier
runs. - Caveat: the model probably fell back to keyword ranking on part of these prompts, after using
up its 1.2 s budget. The run didn't record how many. In later replays of the same prompts, the
model ranked 53 of 60 on a quiet machine and 13 of 60 under load. Details are in
docs/RESULTS.md. Since
#11, the model scores what fits in its budget instead of all or nothing.
We keep the model on. Choosing which code blocks Claude gets, instead of what it would find by
searching and reading, is the decision laya-codex exists to make, and the model makes it far
better than keywords on ranking quality (table above). Turning that into an end-to-end gain is the
top item on the roadmap. To rank by keywords only, set LAYA_CODEX_NO_MODEL=1 or install with--no-model; everything else works the same.
FAQ
What does it cost to run?- Money: nothing. laya-codex is free and runs locally, and it lowers what you pay Claude (−10% per task in the benchmark).
- Disk: about 45 MB for the binaries and about 850 MB for the model.
- Memory: the daemon uses about 1 GB of RAM while it is running.
- Languages: Rust, Python, TypeScript, TSX, JavaScript, Go, Java, C, C++, C#, Ruby, PHP, Kotlin and Swift.
- macOS on Apple Silicon: the full experience, with the model on the Metal GPU.
- Linux x86_64: keyword ranking only by default. The model runs on the CPU there, which is too slow to be useful.
- Other platforms: build from source.
laya-codex itself makes no network calls after installation. The index, the model and the daemon all
live in ~/.cache/laya-codex, which only your user can read, and the local search server
requires a password that laya-codex generates. The code snippets laya-codex adds to a prompt reach
Anthropic as part of your Claude Code conversation, exactly like code Claude reads with its own
tools.
- It never blocks a tool call and never fails a prompt: every hook fails open.
- It never changes Claude's own searches or Reads. It only notes which files Claude read.
- Only git repositories are indexed automatically. Opening Claude Code in your home directory indexes nothing.
laya-codex stop
rm -f ~/.local/bin/laya-codex ~/.local/bin/moon # or: brew uninstall laya-codex
rm -rf ~/.cache/laya-codex
Then, inside Claude Code, run /plugin uninstall laya-codex@laya-codex. If you used laya-codex init,
also remove laya-codex's entries from that repository's .claude/settings.local.json and .mcp.json.
Run laya-codex doctor --repo .. It checks the binary, the search server and its password, the
model, the daemon, the index and the hooks, and prints a fix for anything that fails.
To see exactly what Claude Code and laya-codex exchanged, turn on the trace:
laya-codex trace on # record every hook call and MCP message (off by default)
laya-codex trace show # what was asked, what the daemon ranked, what went back
laya-codex trace show --full --last 1 # the exact JSON in and out, including the injected code
laya-codex trace show --follow # watch live while you use Claude Code
laya-codex trace off && laya-codex trace clear
The trace stays on your machine, in ~/.cache/laya-codex/trace/ (readable only by you), but it
contains your prompts and code, so review it before attaching it to an
issue.
Roadmap
- v0.3.0, current:
- ranking uses the task, not the instructions around it;
- the model ranks within its time budget even under load;
- trust labels on inlined code, and at most two inlined files;
laya-codex tracefor debugging;- a nearly full disk no longer disables laya-codex silently;
- see the CHANGELOG.
- v0.2.0: one name everywhere: the CLI is
laya-codex(waslaya), env vars areLAYA_CODEX_*; a Homebrew formula; the plugin, crash isolation and daemon limits from 0.1.x. - Next:
- smaller injections, so code read plus code injected falls (the −50% token goal);
- fewer tool calls per session, so time falls further (the −30% time goal);
- turning the retrained model's better offline ranking into an end-to-end gain (benchmark v10 shows none yet);
- compacting Moon's data log automatically (it reached 4.1 GB during the benchmark);
- a faster model for Linux.
See ROADMAP.md for the full plan to 1.0.
Reference
Commandslaya-codex init --repo /path/to/repo # add hooks and the MCP server to one repository, then index it
laya-codex doctor --repo /path/to/repo # check everything; prints a fix for each problem (--json available)
laya-codex index /path/to/repo # incremental; re-run any time
laya-codex query "where is WAL replay implemented" --repo /path/to/repo
laya-codex status | laya-codex stop
laya-codex trace on|off|status|show|clear # record Claude Code <-> laya-codex exchanges for debugging
laya-codex init merges laya-codex's hooks and MCP server into the repository's settings:
- It keeps everything else: every other key, hook and server is left alone.
- Re-running it is safe: a second run changes nothing.
- It writes the bare
laya-codexcommand whenlaya-codexonPATHis the binary you ran
(after resolving symlinks, so a Homebrew install writeslaya-codex, not a versioned Cellar path). - It refuses symlinks: it won't write through a symlinked
.claudedirectory or settings file.
Flags:
--dry-runprints the result without writing anything.--no-indexskips indexing.--forcereplaces a settings file that isn't valid JSON; the original is kept as*.bak.
| var | default | meaning |
|---|---|---|
LAYA_CODEX_HOME |
~/.cache/laya-codex |
socket, logs, Moon data and password (moon.acl), models (mode 0700) |
LAYA_CODEX_MODEL_DIR |
laya-code, else laya-base |
model directory |
LAYA_CODEX_NO_MODEL |
unset | 1 = lexical-only ranking |
LAYA_CODEX_BUDGET_MS |
1200 |
Laya time budget per prompt: the model scores as many candidates as fit (lexical only if none) |
LAYA_CODEX_RENDER |
compact |
full injects every span's code |
LAYA_CODEX_WEIGHT / LAYA_CODEX_STATE_TOKENS / LAYA_CODEX_K / LAYA_CODEX_P_THRESHOLD |
0.5 / 128 / 24 / 0 |
ranking knobs (daemon start) |
LAYA_CODEX_ADAPTIVE |
on | 0 = fixed compact injection; default skips code already sent or read in the session, and answers follow-ups about tests or callers with lists instead of code |
LAYA_CODEX_SCORE_TOP |
16 |
candidates the model scores (best first); 0 = all of them |
LAYA_CODEX_MOON_START_SECS |
30 |
how long a freshly started Moon may take to answer |
LAYA_CODEX_MOON_PORT / LAYA_CODEX_MOON_BIN |
16379 / moon beside the real laya-codex binary, else in ../libexec (Homebrew), else on PATH |
Moon sidecar; a missing binary is reported with every path tried |
LAYA_CODEX_BIN |
unset | the laya-codex binary the Claude Code plugin should use |
LAYA_CODEX_TRACE |
unset (laya-codex trace on decides) |
1 = record hook and MCP exchanges in $LAYA_CODEX_HOME/trace/trace.jsonl, a path = record there, 0 = never |
LAYA_CODEX_SCOPE, LAYA_CODEX_SCOPE_P, LAYA_CODEX_TAU_FULL, LAYA_CODEX_TAU_MAP and LAYA_CODEX_SIZE_BY_REPO are no longer read; leaving them set changes nothing.
laya-codex init writes)
.claude/settings.local.json:
{
"hooks": {
"SessionStart": [{"hooks": [{"type": "command", "command": "laya-codex hook", "timeout": 5}]}],
"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "laya-codex hook", "timeout": 8}]}],
"PreToolUse": [{"matcher": "Read|Agent|Task", "hooks": [{"type": "command", "command": "laya-codex hook", "timeout": 5}]}],
"PostToolUse": [{"matcher": "Edit|Write|MultiEdit|NotebookEdit", "hooks": [{"type": "command", "command": "laya-codex hook", "timeout": 5}]}]
}
}
and .mcp.json: {"mcpServers": {"laya-codex": {"command": "laya-codex", "args": ["mcp"]}}} (Claude sees the tool asmcp__laya-codex__search).
Requirements:
- Rust: 1.90+ (edition 2024).
- Moon: a Moon binary built with its
text-indexfeature. laya-codex usesLAYA_CODEX_MOON_BINif set, else looks beside the (symlink-resolved)laya-codexbinary, then in../libexec, then onPATH. - Model weights (optional):
hf download tindang/laya-code --local-dir ~/.cache/laya-codex/models/laya-code. Without them, laya-codex ranks by keywords alone.
cargo build --release -p laya-cli # target/release/laya-codex (fat LTO, mimalloc, Metal on macOS)
cargo test --workspace --release
Reproducing the benchmark
Pinned commits and task sets are listed in docs/RESULTS.md (v8). For each
repository:
python3 bench/run_bench.py tasks --repo <clone> --skip 40 --n 20 [--code-only] --out bench/tasks-v8/<repo>.jsonl
laya-codex index <clone>
python3 bench/run_bench.py run --repo <clone> --tasks bench/tasks-v8/<repo>.jsonl \
--arms baseline,laya-adaptive,laya-lex --turns 2 --out /tmp/bench/<repo>
Then pool the three runs:
python3 bench/stats_pooled.py laya-adaptive baseline /tmp/bench/moon /tmp/bench/httpx /tmp/bench/hono
python3 bench/read_accuracy.py /tmp/bench/moon bench/tasks-v8/moon.jsonl /tmp/bench/httpx bench/tasks-v8/httpx.jsonl \
/tmp/bench/hono bench/tasks-v8/hono.jsonl
Repository layout
| path | what |
|---|---|
crates/laya-core |
shared types, Store/Scorer contracts, code-aware term splitting |
crates/laya-parse |
tree-sitter (14 languages) cAST chunker, symbols, repo walk |
crates/laya-store |
Moon RESP store: OR-BM25 fan-out, circuit breaker, supervisor, auth |
crates/laya-model |
Laya (ModernBERT-large + decision head) in candle, parity-tested |
crates/laya-rank |
candidate generation, Laya gate, fusion, span shaping, rendering |
crates/laya-cli |
laya-codex binary: daemon, hooks, MCP, indexer, init, doctor |
plugin/, .claude-plugin/ |
the Claude Code plugin and its marketplace entry |
install.sh, scripts/ |
installer, chart generator, installer and plugin tests (run in CI) |
finetune/, spike/ |
Laya fine-tuning and the zero-shot spike (Python) |
bench/ |
paired Claude Code benchmark, retrieval eval, results |
License
Apache-2.0. Moon, the search server laya-codex runs, is distributed under its own license
(shipped with its binary). The laya-code model is Apache-2.0 on
Hugging Face. Third-party notices:
NOTICE and docs/release/LICENSES.md.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi