Local preflight for cloud AI coding agents
Project description
LAO — Local AI Orchestrator
Stop paying cloud agents to grep your repo.
Local compute first. Cloud reasoning last.
The Problem
Every time you run Claude Code, Cursor, or Codex on a task, the agent either:
- Gets the entire repo dumped into context (expensive, noisy)
- Has to guess which files are relevant (misses things, wastes tokens)
- Sees your
.envfiles and secrets (dangerous)
LAO solves this locally — before anything reaches the cloud.
What LAO Does
lao prep "fix failing auth test" --repo ./my-repo
In under 2 seconds, locally:
- Scans the repo with ripgrep
- Scores files by relevance (keyword match, import graph, path match, stacktrace parsing)
- Blocks secrets and
.envfiles from leaving your machine - Builds a token-optimized evidence pack for the agent
Result: Claude gets 1.7k tokens of the right context instead of 72k tokens of everything.
Quick Start
# Install
pip install lao
# or: pipx install lao
# Initialize in your repo
cd your-repo
lao init
# Prepare context for a task
lao prep "fix the failing auth session test"
# Output goes to .lao/context-pack.md
# Copy to Claude: cat .lao/context-pack.md | pbcopy
# See how much you save
lao bench
lao init auto-installs ripgrep and gitleaks if missing — just confirm when prompted.
Example Output
lao prep "fix failing auth session test" --output compact
TASK: fix failing auth session test
TYPE:bug_diagnosis DIFFICULTY:cloud_recommended CONFIDENCE:70%
REPO:nextjs/typescript FILES:61 SELECTED:6
TOKENS:1.7k (raw:72k saved:98%)
BLOCKED:.env.local
=== EVIDENCE ===
--- src/auth/session.ts [keyword:15 path:12 error:10] ---
# L38-82
```ts
export function getSession(sessionId: string): Session | null {
const session = sessionStore.get(sessionId)
if (!session) return null
const now = Math.floor(Date.now() / 1000)
// BUG: should be <= not <
if (session.expiresAt < now) {
sessionStore.delete(sessionId)
return null
}
return session
}
--- tests/auth/session.test.ts [keyword:15 path:12 test:4] ---
L28-55
it('returns null for expired session', () => {
vi.setSystemTime(new Date(Date.now() + 3601 * 1000))
const result = getSession(session.sessionId)
expect(result).toBeNull() // FAILS
})
SEND_TO:claude-sonnet
---
## Commands
| Command | Description |
|---------|-------------|
| `lao init` | Initialize lao in current repo |
| `lao prep "task"` | Prepare evidence pack for a task |
| `lao map` | Generate `.ai/` repo infrastructure |
| `lao mcp start` | Start MCP server (for Claude Code/Cursor) |
| `lao mcp config` | Show MCP configuration snippets |
| `lao check` | Check tool availability |
| `lao bench` | Benchmark token savings in dollar terms |
| `lao index` | Build persistent file index (faster prep) |
| `lao stats` | Show last run statistics |
| `lao show` | Show last context pack |
### `lao prep` options
```bash
lao prep "task" [options]
--repo, -r PATH Repo path (default: current directory)
--context, -c TEXT Extra context (paste error output, stacktrace)
--output, -o FORMAT markdown (default) | compact | json
--no-cloud Disable cloud routing recommendation
--verbose, -v Debug logging
Output formats
# Markdown file (default) — for manual copy/paste
lao prep "fix auth bug"
# Compact — token-efficient, for piping to agents
lao prep "fix auth bug" --output compact
# JSON — structured, for tooling
lao prep "fix auth bug" --output json
MCP Integration
LAO exposes three MCP tools so Claude Code, Cursor, and any MCP-compatible agent can call it directly.
Claude Code Setup
Add to .claude/settings.json in your repo:
{
"mcpServers": {
"lao": {
"command": "uv",
"args": ["run", "lao", "mcp", "start"],
"cwd": "/path/to/lao"
}
}
}
Or run lao mcp config to get the exact snippet for your environment.
Available MCP Tools
| Tool | Description |
|---|---|
lao_prep(task, repo_path?, extra_context?, output_format?) |
Prepare evidence pack |
lao_map(repo_path?, force?) |
Generate .ai/ infrastructure |
lao_status(repo_path?) |
Show last run stats |
Cursor Setup
Add to .cursor/mcp.json:
{
"mcpServers": {
"lao": {
"command": "uv",
"args": ["run", "lao", "mcp", "start"]
}
}
}
Codex / Other Agents
# Pipe compact output directly
lao prep "fix auth bug" --output compact | your-agent-cli
.ai/ Repo Infrastructure
lao map generates a .ai/ directory that makes your repo permanently agent-ready.
lao map
Creates:
.ai/
manifest.yaml # Stack, framework, entry points
repo-map.yaml # Module/feature → files mapping
test-map.yaml # Source file → test file mapping
risk-zones.yaml # Sensitive paths and why
agent-rules.md # Rules for AI agents (editable)
Commit .ai/ to git. Every agent that works in the repo benefits immediately.
Example manifest.yaml
repo:
name: my-app
framework: nextjs
primary_language: typescript
test_runner: vitest
package_manager: pnpm
entry_points:
- path: src/app/layout.tsx
type: application
- path: src/cli/index.ts
type: cli
ai_hints:
test_command: npx vitest run
install_command: pnpm install
Example agent-rules.md
# Agent Rules for my-app
## Always
- Run tests before declaring a fix complete
- Keep changes minimal — do not refactor unless asked
- Check .ai/risk-zones.yaml before reading sensitive files
## Never
- Send .env* files or private keys to cloud context
- Add new dependencies without asking
Security
LAO is local-first and privacy-preserving by design:
.env*files are always blocked from leaving your machine- Private keys (
*.pem,*.key,id_rsa) are always blocked - Secret scanning runs locally before any file is included
risk-zones.yamldocuments exactly which paths are sensitive and why- Nothing is sent anywhere — LAO only writes to
.lao/and.ai/locally
Default blocked patterns
.env, .env.*, .env.local, .env.production
**/*.pem, **/*.key, **/*.p12
**/id_rsa, **/id_ed25519
**/credentials*, **/*secret*, **/wallet*
Customize in .lao/config.yaml:
security:
cloud_deny:
- ".env*"
- "**/*.pem"
- "src/internal/pricing/**" # add your own
Configuration
.lao/config.yaml is created by lao init. All fields are optional.
# Token budget for context pack
limits:
max_files_in_context: 8 # Max files selected per run
max_file_tokens: 6000 # Max tokens per file
max_total_context_tokens: 30000
# Cloud routing
cloud:
enabled: true
preferred_model: claude-sonnet
# Local models (optional)
local:
embeddings_enabled: false # Enable with: lao index --with-embeddings
local_llm_enabled: false
How It Works
lao prep "fix failing auth test"
│
├── 1. Classify task → bug_diagnosis, cloud_recommended
├── 2. ripgrep search → 251 matches across 61 files
├── 3. Import graph → session.ts imported by test file (+3.0)
├── 4. Stacktrace parsing → error paths extracted from --context
├── 5. Score files → path_match + keyword_match + entry_point
├── 6. Security policy → .env.local BLOCKED
├── 7. Secret scan → 0 secrets in selected files
├── 8. Build context pack → 6 files, 1.7k tokens
└── 9. Write outputs → .lao/context-pack.md + report.json
Scoring signals
| Signal | Weight | Source |
|---|---|---|
keyword_match |
3.0/match, cap 15 | ripgrep hits in file |
path_match |
6.0/keyword | keyword stem in file path |
error_path |
10.0 | file mentioned in stacktrace |
entry_point |
8.0 / 4.0 | __init__.py, main.py etc. |
import_reference |
3.0/ref | imported by high-scoring file |
repo_map_boost |
5.0 | .ai/repo-map.yaml match |
doc_penalty |
-8.0 | README, CHANGELOG etc. |
generated_penalty |
-8.0 | auto-generated files |
Eval Results
Measured on 20 tasks across two repos (Next.js auth fixture + KMF Python library):
| Metric | Baseline | LAO v0.1 | Target |
|---|---|---|---|
| Context Recall | 60.4% | 78.3% | ≥70% |
| Safety (no secrets) | 100% | 100% | 100% |
| Tasks ≥75% recall | 40% | 50% | — |
Roadmap
| Phase | Focus | Status |
|---|---|---|
| v0.1 | lao prep, ripgrep, security, MCP, index |
✅ Done |
| v0.2 | VS Code Extension, lao index --watch |
🔜 Next |
| v0.3 | GitHub Action, CI/CD integration | 📋 Planned |
| v0.4 | Team dashboard, org policies | 📋 Planned |
| v1.0 | .ai/ community standard, public launch |
📋 Planned |
Requirements
- Python 3.11+
- uv (recommended) or pip
Optional (improves quality):
- ripgrep — faster search
- gitleaks — better secret detection
- fastmcp — MCP server (
pip install fastmcp)
License
MIT
Project details
Release history Release notifications | RSS feed
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 lao-0.1.7.tar.gz.
File metadata
- Download URL: lao-0.1.7.tar.gz
- Upload date:
- Size: 75.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ba2a335d696d6b71a4c368a6813c40ecf5136b5ae0e315190ed51224ba430cb
|
|
| MD5 |
492e896507646ab7734ddff367dc3420
|
|
| BLAKE2b-256 |
2bf69b60eb400cc591e5822a34cd943441325e6cfb38b731063d45a2be24e79a
|
File details
Details for the file lao-0.1.7-py3-none-any.whl.
File metadata
- Download URL: lao-0.1.7-py3-none-any.whl
- Upload date:
- Size: 91.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
773cc3d19e57e7238d59478fbc345df848d5573d2c6cec73fe585af70e2b1a33
|
|
| MD5 |
6bf4f68a4c09dd14a665831c6bfc17d6
|
|
| BLAKE2b-256 |
493c5291d04ccc05ef3636e75ec1ca567c7a49ff83bc7f47f1b995b0ff1ff9c3
|