ashlr

Claude Code-ready. Codex-native. MCP everywhere.

Cut AI coding context without cutting the work.

Ashlr swaps noisy read, grep, edit, and shell output for compact MCP tools. Claude Code gets slash commands, redirects, and a status line; Codex gets plugin packaging, skills, and nudge hooks.

-57%cross-repo token savings
40MCP tools in one router
5supported host paths
Claude CodeCodex / OpenAICursorGooseGeneric MCP

Multi-host by design

Native Codex support, not a compatibility footnote.

CodexPlugin manifest, MCP config, skills, nudge hooks, explorer/worker agents
Claude CodeMarketplace install, slash commands, status line, redirect hooks
Cursor + GoosePortable MCP server with the same 40-tool router
Compare install paths
Token comparison

The same file. 79% fewer tokens.

Without ashlr — raw Read
import { createHash } from "crypto"; import { readFileSync, writeFileSync } from "fs"; import path from "path"; export interface GenomeEntry { id: string; symbol: string; file: string; line: number; kind: "function" | "class" | "interface" | "type" | "const"; summary: string; tokens: number; hash: string; updatedAt: number; } export interface GenomeIndex { version: number; rootDir: string; entries: GenomeEntry[]; totalTokens: number; lastScan: number; } function hashContent(content: string): string { return createHash("sha256").update(content).digest("hex").slice(0, 12); } export function loadIndex(rootDir: string): GenomeIndex | null { const indexPath = path.join(rootDir, ".ashlrcode", "genome", "index.json"); try { const raw = readFileSync(indexPath, "utf8"); return JSON.parse(raw) as GenomeIndex; } catch { return null; } } export function saveIndex(rootDir: string, index: GenomeIndex): void { const dir = path.join(rootDir, ".ashlrcode", "genome"); const indexPath = path.join(dir, "index.json"); writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf8"); } export function mergeEntries( existing: GenomeEntry[], incoming: GenomeEntry[] ): GenomeEntry[] { const map = new Map<string, GenomeEntry>(); for (const entry of existing) map.set(entry.id, entry); for (const entry of incoming) map.set(entry.id, entry); return Array.from(map.values()).sort((a, b) => a.file.localeCompare(b.file)); } export function pruneStale( entries: GenomeEntry[], rootDir: string ): GenomeEntry[] { return entries.filter((e) => { try { const abs = path.resolve(rootDir, e.file); const content = readFileSync(abs, "utf8"); return hashContent(content) !== e.hash || true; } catch { return false; } }); } export function buildSummaryBlock(entries: GenomeEntry[]): string { return entries .map((e) => `[${e.kind}] ${e.symbol} @ ${e.file}:${e.line} — ${e.summary}`) .join("\n"); } export function estimateTokens(text: string): number { // ~3.5 chars per token average for source code return Math.ceil(text.length / 3.5); } export function findBySymbol( entries: GenomeEntry[], symbol: string ): GenomeEntry | undefined { return entries.find( (e) => e.symbol === symbol || e.symbol.endsWith("." + symbol) ); } export function topByTokens( entries: GenomeEntry[], n = 10 ): GenomeEntry[] { return [...entries] .sort((a, b) => b.tokens - a.tokens) .slice(0, n); }
100 KB0 B · 0 tokens
With ashlr — ashlr__read snipCompact
import { createHash } from "crypto"; import { readFileSync, writeFileSync } from "fs"; import path from "path"; export interface GenomeEntry { id: string; symbol: string; file: string; line: number; kind: "function" | "class" | "interface" | "type" | "const"; summary: string; tokens: number; hash: string; updatedAt: number; } export interface GenomeIndex { version: number; [... 3840 bytes elided — 61 lines omitted ...] export function topByTokens( entries: GenomeEntry[], n = 10 ): GenomeEntry[] { return [...entries] .sort((a, b) => b.tokens - a.tokens) .slice(0, n); }
21 KB0 B · 0 tokens
snipCompact: head + tail, middle elided — exact bytes shown in-place
03 · How it works

Three mechanisms. Every file read, every grep, every byte accounted for.

ashlr doesn't compress with magic. It wraps the high-volume tools AI coding hosts already use, applies three concrete techniques at call time, and writes every saving to a local ledger you can audit.

Read it smart

snipCompact head + tail truncation.

Large files come back as head + tail with an elision marker for the middle — the parts agents actually scan. Typical 60 KB source file arrives as ~9 KB. A single call saves ~51 KB.

$ ashlr__read src/auth.ts
// lines 1-24 (head) …
[…43,042 bytes elided…]
// lines 486-510 (tail)
61,840 bytes → 9,203 bytes  −85.1%
Search with memory

Genome-aware grep.

When a .ashlrcode/genome/ is present, ashlr__grep returns pre-summarized sections instead of raw ripgrep output. The agent gets the understanding it needs, not the noise.

$ ashlr__grep 'checkoutSession'
genome → 3 sections, 1.8 KB
rg equiv → 47 matches, 24.1 KB
−92.5% · honest count emitted alongside result
Keep it honest

Live counter. Every saving. Every session.

Every tool call appends to a local ledger under ~/.ashlr/stats.json. The status line ticks up within ~550 ms of each call. Sessions are keyed by the host session id when available, with ASHLR_SESSION_ID as an override, so concurrent terminals never collide.

status line
ashlr · 7d ▁▂▃▄▅▇█ · session +100K · lifetime +4.3M
04Pricing

Free forever. Cloud when you need it.

The free tier is the product. Pro adds hosted infrastructure — it does not remove or degrade anything in Free.

Free
$0forever

The full plugin — every tool, every skill, no strings.

  • +40 MCP tools + Codex skills + Claude slash commands
  • +Local genome scribe loop
  • +Per-session token ledger
  • +Codex + Cursor + Goose support
Start free
Pro
$12per month

Cloud infra for one developer who wants sync and speed.

  • +Everything in Free
  • +Cloud LLM summarizer
  • +Cross-machine stats sync
  • +Live auto-updating badge
Upgrade to Pro
Team
$24per user/month

Shared genome and org-level visibility for engineering teams.

  • +Everything in Pro
  • +Shared encrypted team genome (E2E + vclock)
  • +Org savings dashboard
  • +SSO + SCIM + audit log
Upgrade to Team
05Open source

Auditable to the last byte.