Leaxp
Learn -> experience — a bridge sharing experience between agentic sessions.
Leaxp gives your coding agent a memory it can carry between sessions — and lets you package that memory as a file other projects can install.
Every new agent session starts from zero: it greps the same files, rediscovers the same conventions, and repeats the mistake the last session already worked out. Leaxp fixes that with a per-project knowledge database — .xp/, plain Markdown records indexed by a small local embedding model — that agents search, read and write through four MCP tools.
| Leaxp | |
|---|---|
| Where knowledge lives | Markdown files in .xp/, git-tracked and human-editable |
| Who writes it | your own agent CLI (claude / codex) and your agent at runtime |
| How it is searched | a small local ONNX embedding model — vectors in index.npy |
| What it costs | no LLM API, no API keys, no services, no torch |
| How it travels | .xp files — zip of Markdown + a sha256 manifest, installable anywhere |
The payoff: smaller context, cheaper inference, model-agnostic knowledge, and 3–5 turns of orientation instead of a full-repo grep.
Status: MVP — file and GitHub repository installs work end-to-end; the hosted registry is coming.
How it works
your codebase teammate / another project
│ ▲
│ exp learn / exp forge │ exp install
▼ │
your agent CLI ───────► .xp/local/*.md ───── exp share ─────► my-skills.xp
(claude / codex) (Markdown, yours) (zip + sha256)
│
▼ local embeddings
index.npy + index.json
│
▼ recall / expand / remember / forget (MCP)
your agent, every next session
Install
pip install leaxp
Python 3.11+. Search runs on a small local ONNX embedding model (fastembed) that downloads itself on first use — no torch, no services, no API keys.
Quickstart
Four commands take you from an empty project to an agent with memory.
1. Wire Leaxp into your agent
exp use --with claude
Creates .xp/, leaxp.toml and leaxp.lock, then registers the MCP server and the prompt hook in .mcp.json and .claude/settings.json. Claude Code is the supported target today.
Run it from the environment Leaxp is installed in — the wiring stores that environment's absolute
exppath, because your coding agent runs outside the project's virtualenv. Start a fresh agent session afterwards to pick it up.
2. Teach it your codebase
exp learn --goal "onboard new contributors"
Drives your own claude / codex CLI over the repo. It distills the recurring patterns into one Markdown record per topic under .xp/local/, and Leaxp indexes whatever appeared. The records are yours: git-tracked, reviewable in a PR, editable in your editor.
3. Install skills from GitHub
exp install https://github.com/org/repo --skill name
Pulls a repository's SKILL.md skills into .xp/share/org-repo/ and declares the package in leaxp.toml + leaxp.lock. Drop --skill to install every skill the repo publishes.
4. Upgrade an installed package
exp install https://github.com/org/repo --upgrade
Re-resolves the source to its newest version and moves the lock to it. Without --upgrade, installs replay the locked version, so they stay reproducible.
That is the whole loop. From here your agent gets a memory recall on its first prompt of every session and writes back what it learns.
Repository knowledge management
Day-to-day upkeep once memory is in place.
| Command | Reach for it when |
|---|---|
exp sync --upgrade |
you pulled a leaxp.toml change, edited records by hand, or want newer skill releases |
exp share -o pkg.xp |
you want to hand your project's knowledge to a teammate or another repo |
exp install -f pkg.xp |
you received an .xp file and want it in this project |
exp exec "…" |
you want to see exactly what an agent gets back for a query |
exp forge |
memory has drifted: duplicates, stale statements, rules buried in long records |
Keep dependencies and the index current
exp sync # install declared packages at their locked version, then reindex .xp/
exp sync --upgrade # ...and check every unpinned package for a newer version
Three steps in one pass: install every [[packages]] entry that is missing or drifted from the lock → with --upgrade, re-resolve unpinned packages against their source → reindex .xp/, so hand-edited records become searchable too.
Share your project's knowledge
exp share -o my-project.xp
Packs .xp/local/ into a portable .xp file — a zip of the Markdown plus a sha256-signed manifest. Embeddings are not shipped; they are recomputed on install, so the same pack works with any embedding model.
Install from a file
exp install -f my-project.xp # → .xp/share/<name>/ — a read-only dependency
exp install -f my-project.xp --self # → .xp/local/ — adopted as your own, editable
See exactly what your agent sees
exp exec "create stripe subscription"
exp exec "define new cli subcommand" -s "error handling" -n 5
Prints the raw recall tool output, byte for byte what a connected agent receives — the fastest loop for tuning record titles and descriptions.
Consolidate memory that drifted
exp forge
remember keeps the database consistent one write at a time, but a long series of sessions still drifts: near-duplicates accumulate, statements go stale against the code they describe, and rules stay buried in long records instead of being flagged as conventions.
exp forge is the periodic repair pass. It drives your own agent through a consolidation flow:
- cluster
.xp/local/by file name; - write the clusters down as a plan in
.xp/consolidation-<dtime>.md; - walk that plan — grounding every statement in the real codebase, merging duplicates found via
recall, splitting rules out as one-paragraph conventions; - run the full
exp syncpass, so the reshaped memory is reindexed.
The plan file doubles as a review log: every cluster gets a Consolidation Result, plus Consolidation Gaps where the agent noticed knowledge that is missing.
What your agent sees
Once wired, agents get an initial memory recall on their first prompt and four tools:
| Tool | What it does |
|---|---|
recall(query, subquery?, limit?) |
Search records. Returns scored titles + expected outcomes, grouped into <package> blocks, low-relevance hits already filtered out. Conventional records come fully expanded as <rule> entries in a separate <conventions> block. |
expand(id | [ids]) |
Fetch full Markdown bodies — one record or several at once. |
remember(title, description, content, id?, convention?) |
Write new knowledge, or update a record in place by passing its id. Pass convention=true for a one-paragraph rule. |
forget(id | [ids]) |
Delete records that became duplicates or went out of date. |
Two things make this a consistent memory rather than an append-only log:
rememberreviews itself. Its answer replays arecallagainst the title just written, so duplicates and stale neighbours surface at write time instead of piling up — the agent is told to merge them andforgetthe leftovers.- Only
localis writable.rememberandforgetrefuse ids from installed packages and say why; shared records are dependency content thatexp syncwould restore anyway.
Concepts
Record
One Markdown file with YAML frontmatter, under .xp/local/ (yours, git-tracked) or .xp/share/<pkg>/ (installed, git-ignored):
---
index: create stripe subscription
description: Know which helper to call and which webhook confirms the charge
tags: []
---
# create stripe subscription
Call `billing.subscribe(customer, plan)` — never the Stripe SDK directly.
## Confirming the charge
...
Titles follow [action] [package|entity] [subject?] — create stripe subscription, define new cli subcommand, downgrade alembic migration — so queries and titles speak the same language. One topic per file; 5–20 focused records beat one huge dump.
Convention
A record flagged convention: true: a single short paragraph stating one rule (naming, structure, which tool or skill to use) plus its rationale. Conventions are searched separately and rendered in full on every matching recall, no expand needed — which is how a project rule gets followed instead of merely found.
Index
index.npy + index.json. Exactly one vector per Markdown-heading breadcrumb (Stripe API > Webhooks > Error Handling), searched by cosine similarity with a relevance floor so agents never wade through noise. Results are grouped by package and labelled with its description — from leaxp.toml for local, from the install manifest for shared packages.
Record ids are row numbers, and rows never move: rewriting a record reuses its rows, forgetting one tombstones them. An id an agent recalled keeps pointing at the same record until the next full reindex.
.xp file
A zip of record Markdown plus a sha256-signed manifest (xp_version, name, version, digest, record counts). Portable and model-agnostic — embeddings are recomputed locally on install.
Dependency
A [[packages]] entry in leaxp.toml (what you declare) resolved into a leaxp.lock entry (what is installed: version, exact ref, digest). exp sync installs whatever is missing or drifted at the locked version; --upgrade re-resolves each unpinned package.
Skill repos have no versioning convention, so a GitHub package's version is its newest release tag (v1.2.3) when it publishes one, and the default branch's commit sha otherwise. Pin a specific one with /tree/<ref> in the source or version = "…" in leaxp.toml — --upgrade leaves pins alone.
Wiring
exp use writes .mcp.json and .claude/settings.json with the absolute launcher of the environment it ran in (<venv>/bin/exp mcp, or <python> -m leaxp mcp when no console script exists). A bare exp would only work for a global install, since your coding agent runs outside the project's virtualenv.
That makes the wiring machine-specific: after cloning, run exp use once in your own environment — it repoints a stale command instead of adding a second one.
What lives where
your-project/
├── leaxp.toml # what you declare: project meta, models, packages
├── leaxp.lock # what is installed: version, exact ref, digest
├── .mcp.json # MCP wiring (absolute launcher) — written by `exp use`
└── .xp/
├── .gitignore # written by `exp use` — tracks local/, ignores everything else
├── local/ # your records — git-tracked, editable, the only writable package
│ └── create-stripe-subscription.md
├── share/ # installed packages — git-ignored, read-only
│ └── org-repo/
├── index.npy # one vector per heading breadcrumb
├── index.json # record metadata + package descriptions
├── .sessions/ # once-per-session hook markers
└── consolidation-*.md # one review log per `exp forge` run
Command reference
| Command | What it does |
|---|---|
exp use --with claude |
Create .xp/, config and lock; wire the MCP server + prompt hook. |
exp learn [--goal …] [--agent …] |
Drive your agent CLI over the codebase into .xp/local/, then index. |
exp forge [--agent …] |
Consolidate .xp/local/ — verify, dedupe, split conventions — then sync. |
exp share -o pkg.xp |
Pack .xp/local/ into a shareable .xp archive. |
exp install <url|name> [--skill …] [-U] |
Install from GitHub or the registry; index and lock it. |
exp install -f pkg.xp [--self] |
Install from a local archive; --self adopts it into .xp/local/. |
exp sync [-U] |
Install declared packages, optionally upgrade, reindex .xp/. |
exp exec "<query>" [-s …] [-n …] |
Print the raw recall output for a query. |
exp mcp [-t stdio|http] |
Run the MCP server (your agent does this for you). |
exp --version |
Print the installed version. |
Every command accepts --path to target a project directory other than the current one.
Docs
| Document | Contents |
|---|---|
| CLAUDE.md | Package architecture overview — modules, data flow, key abstractions |
| AGENTS.md | Ground rules for coding agents working on this repo |
| CHANGELOG.md | Per-version release notes |
| RELEASING.md | How a new version gets published |
Development
uv sync # create venv, install deps
uv run pre-commit install # enable hooks
uv run ruff check . && uv run pyright # lint + typecheck (pyright strict)
uv run pytest --cov # tests with coverage (100% gate)
uv run exp --help
License
Apache-2.0 — see LICENSE.txt.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file leaxp-0.4.0.tar.gz.
File metadata
- Download URL: leaxp-0.4.0.tar.gz
- Upload date:
- Size: 84.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbb705d758989d1e1f2d91103d73bfb2154823f5815b5425dbc60f5cd12d09ce
|
|
| MD5 |
8ed2bd561e5d7f80f3fb026dffc4156b
|
|
| BLAKE2b-256 |
c6b1af168e9f73dfc77df271edd5e309cd7c47ca7fc60b40bd97000465669fed
|
Provenance
The following attestation bundles were made for leaxp-0.4.0.tar.gz:
Publisher:
release.yml on vunone/leaxp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leaxp-0.4.0.tar.gz -
Subject digest:
cbb705d758989d1e1f2d91103d73bfb2154823f5815b5425dbc60f5cd12d09ce - Sigstore transparency entry: 2290699927
- Sigstore integration time:
-
Permalink:
vunone/leaxp@8a80f4454be83d5f3623d8a15373e7968b8b7dde -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/vunone
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8a80f4454be83d5f3623d8a15373e7968b8b7dde -
Trigger Event:
push
-
Statement type:
File details
Details for the file leaxp-0.4.0-py3-none-any.whl.
File metadata
- Download URL: leaxp-0.4.0-py3-none-any.whl
- Upload date:
- Size: 83.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff9f681f7478d521928ac8404846e729372fc3b043e5ea2030ebc2c09f1037b2
|
|
| MD5 |
68c9303ac594c1c3019fc2e66ff22e0c
|
|
| BLAKE2b-256 |
44dfc0cdd08d46f6e296b8bbb57913b2f212f799a914a3c3dc1ea4d93c88d59a
|
Provenance
The following attestation bundles were made for leaxp-0.4.0-py3-none-any.whl:
Publisher:
release.yml on vunone/leaxp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leaxp-0.4.0-py3-none-any.whl -
Subject digest:
ff9f681f7478d521928ac8404846e729372fc3b043e5ea2030ebc2c09f1037b2 - Sigstore transparency entry: 2290699979
- Sigstore integration time:
-
Permalink:
vunone/leaxp@8a80f4454be83d5f3623d8a15373e7968b8b7dde -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/vunone
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8a80f4454be83d5f3623d8a15373e7968b8b7dde -
Trigger Event:
push
-
Statement type: