laya-codex

mcp
Guvenlik Denetimi
Uyari
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.

SUMMARY

Ranked code retrieval for Claude Code: tree-sitter + BM25 + the Laya re-ranker, delivered through hooks and MCP

README.md

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.

Latest release
CI
License: Apache-2.0
Model on Hugging Face
Claude Code plugin

With laya-codex, Claude Code takes 33.6% fewer turns, 12.0% less wall-clock time, 19.8% fewer output tokens, 21.7% fewer input tokens and costs 8.8% less; it reads 32.0% less code, but code read plus code injected shows no significant change (+6.2%) (paired benchmark v10, 51 tasks on three repositories, 95% confidence intervals)

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 ..

Other ways to set it up
  • One repository only, without the plugin: laya-codex init --repo /path/to/repo writes laya-codex's hooks into
    that repository's .claude/settings.local.json and 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-only fetches and verifies only the model and leaves the binaries alone; use it after brew install;
    • --version vX.Y.Z installs a specific release;
    • --dir DIR installs somewhere other than ~/.local/bin;
    • --no-model skips the ~850 MB model, and laya-codex ranks by keywords alone;
    • --model downloads 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: init writes through a symlinked .claude folder 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:

Share of tasks with a correct file in Claude's context by turn: with laya-codex 77% before the first turn (median turn 0); stock Claude Code starts at turn 3 (median 5.5) Read behaviour: precision 0.59 → 0.62, relevant code found 82% → 91%, first relevant Read at turn 5.9 → 3.3, wasted read tokens 2,005 → 1,187

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
Scatter of 180 benchmark sessions: wall-clock time rises about 10.7 s per 1,000 output tokens (R² 0.92), with stock and laya-codex sessions on the same line Characters added to the second prompt: moon 4,554 → 1,842, httpx 4,131 → 1,474, hono 3,926 → 1,443 (v0.3.0 → now)

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 (server laya-codex). Given a
    name (or a|b, optionally with a path), 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.
Which languages and platforms are supported?
  • 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.
Is my code sent anywhere?

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.

Will it get in Claude's way?
  • 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.
How do I uninstall it?
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.

Something isn't working

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 trace for 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 (was laya), env vars are
    LAYA_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

Commands
laya-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-codex command when laya-codex on PATH is the binary you ran
    (after resolving symlinks, so a Homebrew install writes laya-codex, not a versioned Cellar path).
  • It refuses symlinks: it won't write through a symlinked .claude directory or settings file.

Flags:

  • --dry-run prints the result without writing anything.
  • --no-index skips indexing.
  • --force replaces a settings file that isn't valid JSON; the original is kept as *.bak.
Configuration (environment variables)
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.

Manual hook setup (what 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 as
mcp__laya-codex__search).

Building from source

Requirements:

  • Rust: 1.90+ (edition 2024).
  • Moon: a Moon binary built with its text-index feature. laya-codex uses LAYA_CODEX_MOON_BIN if set, else looks beside the (symlink-resolved) laya-codex binary, then in ../libexec, then on PATH.
  • 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)

Sonuc bulunamadi