CodeYantra reads your codebase, proposes safe patches, and iterates with you — without a single byte ever leaving your machine.
"Walk me through this repo." · "Why does every item show up as 'other' in @categorize.py?" · "Add a --csv flag and update the summary." · "Review my current git diff — anything wrong?" CodeYantra answers, refactors, writes tests, and drafts patches from prompts like these using a local coder model (Qwen / DeepSeek / CodeLlama) via Ollama. It has file-tree awareness, git awareness, an approval-gated patch engine, and a quality checker that runs before every apply — all on your device.
Free local AI · No cloud · No accounts · No telemetry · No token bill. Part of the Free AI. Real Problems. Every Sunday. series.
Repo layout:
apps/desktop/(Tauri + React shell),packages/shared/(types, zod validation),packages/agent/(prompts, tool spec, patch quality engine),crates/localcode-core/(Rust — workspace, git, patch, search, security, terminal),docs/(capabilities, guardrails, install, architecture),scripts/(dev-clean, reinstall).
- What CodeYantra solves
- Why local AI matters (especially for code)
- How local AI coding works
- Ollama context vs. workspace overview
- The seven agent modes
- Architecture
- Tech stack
- Folder structure
- Setup
- Supported operating systems
- Privacy model
- Data storage
- Guardrails
- Hard limits
- Demo script
- Example prompts
- Roadmap
- Limitations
- Contributing
- License
The best AI coding tools (Cursor, GitHub Copilot, Claude Code) are excellent — but every keystroke, every open file, every prompt goes to someone else's servers. And they cost $10–$20 per developer, per month, forever. For anyone working on private repos, client codebases, health / finance / defence code, or just anyone tired of the running bill, that's a non-starter.
CodeYantra is the same class of tool — chat with your codebase, refactor across files, write tests, review your diff — running entirely on your laptop through Ollama. Your code stays where it is. The only network traffic the app produces is to localhost:11434.
Your codebase is the most sensitive data you own — credentials in comments, business logic, algorithms, client secrets, unshipped features. CodeYantra is built so this data never leaves your device:
- No OpenAI / Anthropic / cloud APIs. No hosted LLM. No hosted database. No telemetry.
- The only "AI" is a local Ollama model on
localhost:11434. - Nothing writes to disk without your explicit click. Nothing overwrites without a rollback snapshot first.
- The Rust core enforces path-safety, size caps, and binary rejection before the model ever sees a byte.
Local isn't a limitation — it's the whole point.
┌─────────────── you type a prompt ───────────────┐
│ "Fix the case-sensitivity bug in @categorize.py" │
└─────────────────────┬─────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ 1. PRELOAD workspace overview → file tree + extension histogram │
│ + README + git status │
│ 2. RESOLVE @-mentions → paste referenced file contents │
│ 3. STREAM from local Ollama → assistant reasoning + tool calls │
│ 4. RUN tools (read/list/search) → up to 14 iterations per turn │
│ 5. PARSE fenced patch envelope → auto-normalise EOL/newline │
│ 6. QUALITY-CHECK the patch → imports, defs, brackets, no-ops │
│ 7. SHOW Review modal + diff → you approve, reject, or partial │
│ 8. SNAPSHOT then atomic write → rollback-able, crash-safe │
└───────────────────────────────────────────────────────────────────┘
Every step is deterministic Rust or pure-JS. The LLM only proposes; it never writes.
CodeYantra does not stream your whole repo to the model. The model sees only the overview + files you
@-mention + files it explicitly reads. Everything else stays on disk, indexed by the Rust workspace walker.
Local coder models have context windows of 8K–32K tokens — enough for a handful of files, not a whole repo. So CodeYantra treats Ollama purely as a reasoning layer. On every turn, the app preloads a compact workspace overview (file tree summary, extension histogram, README preview, git status) into the system prompt. The model can then request specific files via read_file — up to the iteration budget for that mode. Anything you @-mention in the chat gets pasted verbatim (with a per-file budget) into that same prompt, so the model doesn't waste a turn reading it.
Every mode reshapes the system prompt and iteration budget for its job. Pick from the dropdown above the chat.
| Mode | Writes? | Best for |
|---|---|---|
ask |
✗ | Read-only Q&A. "What does this project do?" |
edit |
✓ | Focused single-file / few-file edits. |
agent |
✓ | Multi-step: read → plan → patch. Chains up to 14 tool calls. |
review |
✗ | Reads current git diff, comments on correctness and risk. |
test |
✓ | Writes tests matching your framework. |
refactor |
✓ | Behaviour-preserving structural improvements. |
explain |
✗ | Long-form walkthroughs — great for onboarding and docs generation. |
You type a prompt in the chat ──┐
▼
ChatPanel (React) ── loads workspace overview + @-mentions
│
▼
agentRunner (TS) ── streams from Ollama, parses tool calls / fenced patch
│ emits events: assistant_delta · tool_call_start · tool_call_done · patch_proposal
▼
Tauri IPC bridge (invoke)
│
▼
Rust command layer (apps/desktop/src-tauri/src/lib.rs)
│
▼
localcode-core (crates/localcode-core)
├── workspace.rs file tree + read_file (5 MiB cap, binary reject)
├── security.rs PathPolicy — canonicalised path guard
├── patch.rs atomic writes + snapshot + rollback
├── git.rs status + diff
├── search.rs ripgrep-backed workspace search
└── terminal.rs approval-gated command runner
│
▼
Local filesystem + Ollama @ localhost:11434
(path-guarded) (qwen3-coder / deepseek / codellama)
Two design choices anchor everything:
- Deterministic math, LLM for words. Filesystem writes never go through the model — they go through the Rust patch engine. Every write is atomic (tmp + fsync + rename) and preceded by a snapshot.
- Policy checks before action.
PathPolicy::checkruns on every fs op;rm -rf /doesn't get pastterminal.rsrisk classification.
| Layer | Technology |
|---|---|
| Desktop shell | Tauri 2 (Rust bootloader + WebKit web view) |
| UI | React 18, Vite 5, TypeScript 5, Tailwind CSS, Zustand + persist middleware |
| Fonts | Inter (UI), JetBrains Mono (code) — loaded from Google Fonts |
| Core | Rust 1.75+ — ignore (gitignore walker), diffy (unified diff apply), git2 (git status/diff), tokio (async), serde, sha2 |
| Shared types | Zod (runtime validation of assistant JSON + fenced patch envelope) |
| Local LLM | Ollama on localhost:11434 — defaults to qwen3-coder:latest, any model works |
| Packaging | Tauri bundler → .dmg (macOS), .msi + NSIS .exe (Windows), .deb + .AppImage (Linux) |
| CI | GitHub Actions (tauri-apps/tauri-action@v0) — matrix over mac/win/linux |
No cloud APIs. No hosted DB. No telemetry.
codeyantra/
├── apps/
│ └── desktop/
│ ├── src/ # React UI
│ │ ├── App.tsx # layout + rehydration
│ │ ├── components/ # ChatPanel · EditorPane · ExplorerPanel ·
│ │ │ # PatchApprovalModal · WorkspaceSwitcher · Logo
│ │ ├── lib/ # tauri.ts · store.ts (Zustand) · ollama.ts ·
│ │ │ # agentRunner.ts · tools.ts · mentions.ts ·
│ │ │ # workspaceOverview.ts
│ │ └── styles/index.css
│ ├── src-tauri/ # Rust command layer
│ │ ├── src/lib.rs # #[tauri::command] fns
│ │ ├── icons/ # app icons (baked into installers)
│ │ └── tauri.conf.json
│ └── public/logos/ # favicon + brand mark PNGs
├── packages/
│ ├── shared/ # Types + zod validators (PatchProposal, ToolCall, ...)
│ └── agent/ # Prompts, tool spec, fenced patch parser,
│ # patch quality engine (patchChecks.ts)
├── crates/
│ └── localcode-core/ # workspace · git · patch · search ·
│ # security · terminal
├── docs/ # CAPABILITIES · GUARDRAILS · INSTALL · architecture
├── scripts/ # dev-clean · reinstall (macOS daily driver)
├── .github/workflows/build.yml # cross-platform installer matrix
├── CLAUDE.md # project engineering rules
├── LICENSE # MIT
└── README.md
- Node.js 18+ and pnpm 9+
- Rust 1.75+ (
rustup default stable) - Ollama installed and running (
ollama serve) - A pulled coder model:
ollama pull qwen3-coder
pnpm install
pnpm devThis installs JS deps, boots Vite dev server, and launches the Tauri shell with live HMR. The first launch triggers a full Rust build of localcode-core and the Tauri command layer — expect 5–15 minutes. Subsequent launches are near-instant.
./scripts/reinstall.shKills any running instance, runs pnpm tauri build, swaps /Applications/CodeYantra.app, and relaunches. From then on: Cmd+Space → "CodeYantra".
pnpm tauri build also produces installers for the current platform under apps/desktop/src-tauri/target/release/bundle/. For cross-platform builds, use the checked-in GitHub Actions workflow — see docs/INSTALL.md.
| OS | Installer | Status |
|---|---|---|
| macOS | .dmg (Apple Silicon + Intel via CI matrix) |
✓ |
| Windows | .msi + NSIS .exe |
✓ |
| Linux | .deb + .AppImage |
✓ |
Unsigned installers show a one-time "unidentified developer" dialog (macOS Gatekeeper) or "Windows protected your PC" (SmartScreen). Both have documented bypass steps in docs/INSTALL.md.
- No outbound network calls except to
http://localhost:11434(Ollama) and to your local filesystem. Grep the Rust crate forreqwest/hyper::Client— there are none besides the OS-levelgitbinary. - No telemetry, no crash reporters, no analytics.
- No auth, no accounts, no sign-in.
- Fonts (Inter, JetBrains Mono) load once from Google Fonts and cache; disable via the Tauri CSP if you want zero external requests.
- Persistent app state lives in
localStorage(workspace, tabs, chat per workspace, mode, AI config) — inside the Tauri WebKit sandbox, on your device only.
- Snapshots per patch: every applied patch writes the previous state to
.codeyantra/snapshots/<snapshot-id>/inside your workspace, so a bad apply can be rolled back. - Persisted UI state: last-opened workspace, per-workspace open tabs and chat history, agent mode, model / temperature, activity view — stored in the Tauri WebKit
localStorageunder keycodeyantra:v1. - No workspace content stored outside your project. File contents are read on demand and not persisted anywhere by CodeYantra.
Every safety mechanism is documented and grep-able. Full deep-dive: docs/GUARDRAILS.md.
| Vector | Defence |
|---|---|
| Path escaping the workspace | security::PathPolicy — canonicalised, descendant-of-root |
Reading ~/.ssh, ~/.aws, etc. |
Blocked by default in PathPolicy.blocked |
rm -rf /, mkfs, dd if= |
Hard-blocked in terminal.rs command classifier |
git push, terraform apply |
Requires explicit user approval per invocation |
| Half-written files on crash | atomic_write — tmp file → fsync → rename |
| Bad patch destroying files | Snapshot before touch; auto-rollback on any per-file error |
| File > 5 MiB entering context | read_file command hard-caps |
| Binary file entering context | Null-byte scan on first 8 KiB rejects |
| Model exfiltration to cloud | Provider layer only calls Ollama on localhost |
| Prompt injection via file contents | System prompt tells the model to treat file contents as data |
| Model hallucinating dropped code | Quality engine checks imports + top-level defs preserved |
| React error crashing the app | RootErrorBoundary + window.onerror + unhandledrejection |
| Limit | Value | Enforced in |
|---|---|---|
| Max file size (read/edit) | 5 MiB | apps/desktop/src-tauri/src/lib.rs::read_file |
| File-tree crawl depth | 6 | apps/desktop/src/lib/tools.ts::listFiles |
| Search results per call | 200 | apps/desktop/src/lib/tools.ts::searchText |
| Tool result truncation | 12 000 chars | apps/desktop/src/lib/tools.ts::MAX_RESULT_CHARS |
@-mention per-file budget |
6 000 chars | apps/desktop/src/lib/mentions.ts |
@-mention total budget |
20 000 chars | apps/desktop/src/lib/mentions.ts |
| Agent iterations per turn | 10–14 (mode) | apps/desktop/src/components/ChatPanel.tsx |
| Workspace overview sample | 80 tree entries | apps/desktop/src/lib/workspaceOverview.ts |
Full spec: docs/CAPABILITIES.md.
A ready-made demo repo lives at ~/codeyantra-demo/receipts — a small Python CLI that parses grocery receipts and applies loyalty discounts. It has a deliberate case-sensitivity bug in categorize.py, a giant if/elif chain that begs to be refactored, no docstrings in summary.py, no tests for discount.py. Open it in CodeYantra and walk through the seven modes in order — the full tour takes 10–15 minutes.
"What does this project do? Walk me through main.py and how the modules fit together."
ask mode — one answer from the preloaded workspace overview, no tool spree.
"Look at @categorize.py — how does it decide which category an item belongs to?"
@-mention picker opens; pick the file; contents inject straight into the prompt.
"When I run python main.py sample_receipts/receipt1.txt, every item shows up as 'other'. Something in @categorize.py is wrong. What is it and why?"
explain mode — traces the bug, no patches.
"Fix the case-sensitivity bug in @categorize.py by lowercasing the item name before matching. Keep the existing structure."
edit mode — patch proposed, Review modal opens with before/after diff, quality engine flags any dropped imports or definitions.
"Refactor @categorize.py — the giant if/elif chain should be a dict lookup for clarity."
refactor mode — behaviour-preserving. The quality engine's "top-level definitions preserved" check protects against the model dropping categorize() by accident.
"Write pytest tests for @discount.py covering bronze, silver, gold, platinum, and the >$50 gold bonus."
test mode — creates a new test file.
"Add a --csv <path> flag to @main.py that also writes the summary as CSV. Update parse.py/summary.py if needed. Don't break the existing text output."
agent mode — chains up to 14 tool calls; single multi-file patch.
"Review my current git diff. Any bugs, unclear names, or missed edge cases?"
review mode after you make a change — read-only commentary.
- Phase 1 — Tauri shell, seven modes, Ollama streaming
- Phase 2 — Human-gated patch engine + snapshots + rollback
- Phase 3 —
@-mention picker, workspace overview preload, patch quality engine - Phase 4 — Multi-workspace switcher, per-workspace state persistence
- Phase 5 — Auto-run tests / lints and close the loop (terminal exists; agent wiring pending)
- Phase 6 — Language server integration (jump-to-def, types, live diagnostics)
- Phase 7 — Live SQL / execution oracle (DuckDB row-diff for SQL edits)
- Phase 8 — Command palette + custom keyboard shortcuts
- Phase 9 — Cross-repo transformations
- Phase 10 — Signed installers (silence Gatekeeper / SmartScreen)
- Model-bound quality. CodeYantra is a workspace, not a model. Output quality depends on your Ollama model. Tier 1 languages (TS/JS/Py/SQL/HTML/CSS/JSON/YAML) work great; Tier 3 (Kotlin/Scala/Dart/Haskell) work but need smaller, well-scoped asks.
- No cross-repo refactors yet. Workspace scope is a single folder. You can switch between recent workspaces, but one turn = one repo.
- No auto-run tests yet. The terminal exists and commands respect the approval gate, but the agent doesn't automatically kick off
pytestafter applying a patch. Coming in Phase 5. - Not a real code editor. The editor pane is a plain textarea with a line-number gutter, not Monaco / CodeMirror. Fine for reading and small edits; use your usual editor for extended work.
- Unsigned installers. First launch on macOS shows a Gatekeeper warning (right-click → Open bypasses it), Windows shows SmartScreen. Signing certificates cost money.
PRs welcome. Design rules any PR must respect:
- Deterministic math, LLM for words. Filesystem writes never go through the model — they go through the Rust patch engine.
- Local-first. No new outbound network call without an explicit user toggle.
- Policy checks before action. Every filesystem or shell operation goes through
security.rsfirst. - Small patches. Bias toward many small, verifiable changes over one big rewrite.
Setup:
pnpm install
pnpm --filter @localcode/desktop typecheck
cargo check -p localcode-core
pnpm devFull engineering rules: CLAUDE.md.
MIT — see LICENSE. Free to use, modify, fork, sell.
Built as part of the Free AI. Real Problems. Every Sunday. series. Made with Rust, TypeScript, and stubbornness about privacy.