Overview
BE LAZY is a VS Code extension that embeds an autonomous AI coding agent directly into your editor sidebar. You describe a task in plain English — the agent reads your codebase, reasons about what needs to change, and writes the files. Every modification is checkpointed so you can revert the entire task with one click.
No context switching. No copy-pasting. Just describe and ship.
Features
- Agentic loop — the agent iterates up to 30 steps, chaining
read_file → edit_file calls until the task is complete
- Multi-provider — works with OpenAI and OmniRoute out of the box; new providers plug in with a single class
- Image input — attach or paste screenshots directly into the chat; the agent uses them as visual context
- Checkpoint & revert — every task snapshots affected files before touching them; one click restores the workspace to its exact prior state
- Diff viewer — click any changed file to open a side-by-side diff of original vs. current in the VS Code diff editor
- Secure by default — path traversal protection, HTTPS-only API calls, strict Content Security Policy on the webview, API keys stored in VS Code SecretStorage
Tech Stack
| Layer |
Technology |
Purpose |
| Extension host |
TypeScript 5.3, Node.js |
Core agent logic, file I/O, VS Code API |
| Webview UI |
React 18, Vite 5 |
Chat and settings interface rendered inside VS Code |
| AI providers |
OpenAI API (compatible) |
LLM inference via streaming chat completions |
| Persistence |
VS Code SecretStorage |
Encrypted API key storage |
| Checkpoints |
Filesystem (os.tmpdir) |
Crash-safe task snapshots for revert |
| Packaging |
@vscode/vsce |
Extension packaging and Marketplace publishing |
Architecture
be-lazy/
├── src/ # Extension host (Node.js / VS Code API)
│ ├── extension.ts # Activation entry point
│ ├── agent/
│ │ └── Agent.ts # Agentic loop — drives the LLM + tool execution
│ ├── providers/
│ │ ├── Provider.ts # AIProvider interface + shared types
│ │ ├── BaseProvider.ts # Shared HTTP client (streaming, timeout, HTTPS enforcement)
│ │ ├── OpenAIProvider.ts # OpenAI identity (extends BaseProvider)
│ │ ├── OmniRouteProvider.ts # OmniRoute identity (extends BaseProvider)
│ │ └── ProviderRegistry.ts # Provider lookup + registration
│ ├── tools/
│ │ ├── Tool.ts # ITool interface
│ │ └── FileTools.ts # read_file + edit_file tools (path traversal protected)
│ ├── changes/
│ │ └── CheckpointManager.ts # Snapshot, persist, and revert file changes
│ └── ui/
│ ├── ChatProvider.ts # WebviewViewProvider — bridges extension ↔ webview
│ └── SettingsProvider.ts # Provider config + SecretStorage management
│
└── webview/ # Webview UI (React + Vite, compiled to dist/)
└── src/
├── main.tsx # React root + ErrorBoundary
├── App.tsx # State machine (useReducer) + message routing
├── ChatView.tsx # Chat UI — messages, input, image attach, changes panel
├── SettingsView.tsx # Provider/model configuration form
└── vscodeApi.ts # postMessage / onMessage bridge to extension host
How It Works
Request lifecycle
User types a message
│
▼
ChatView.tsx ──postMessage──▶ ChatProvider.ts
│
validates input
(length, image count)
│
▼
Agent.ts
┌─────────────────────────────┐
│ while iterations < 30 │
│ │
│ provider.chat(messages) │
│ │ │
│ ▼ │
│ BaseProvider (HTTPS stream) │
│ │ │
│ parse SSE chunks │
│ │ │
│ tool_calls? ──yes──▶ execute tool
│ │ │
│ no │
│ ▼ │
│ break │
└─────────────────────────────┘
│
emit AgentEvents
│
▼
ChatProvider.ts ──postMessage──▶ App.tsx
│
update UI state
Checkpoint lifecycle
First edit_file call in a task
│
▼
CheckpointManager.createCheckpoint(taskId)
│
▼
For each file before it is written:
CheckpointManager.snapshotFile(taskId, absolutePath)
→ reads current content (or records null if file is new)
→ writes snapshot to os.tmpdir/belazy-checkpoints/<taskId>.json
│
▼
User clicks "Revert Task"
│
▼
CheckpointManager.revertTask(taskId)
→ restores each file to its snapshot content
→ deletes files that were newly created by the task
→ removes the checkpoint JSON from disk
Provider model
AIProvider (interface)
│
└── BaseProvider (abstract)
├── HTTPS enforcement
├── SSE streaming parser
├── 60s request timeout
└── requestJson helper
│
├── OpenAIProvider (id: "openai", baseUrl: api.openai.com)
└── OmniRouteProvider (id: "omniroute", baseUrl: api.omniroute.ai)
Adding a new provider takes 5 lines:
import { BaseProvider } from './BaseProvider';
export class AnthropicProvider extends BaseProvider {
readonly id = 'anthropic';
readonly name = 'Anthropic';
readonly defaultBaseUrl = 'https://api.anthropic.com/v1';
}
Then register it in ProviderRegistry.ts.
Security
| Concern |
Mitigation |
| Path traversal |
resolvePath() rejects any path that resolves outside workspaceRoot() |
| API key exposure |
Keys stored in VS Code SecretStorage (OS keychain backed); never logged or sent to the webview |
| Cleartext API calls |
BaseProvider rejects any base URL that does not start with https:// |
| Webview XSS |
Strict CSP injected at runtime: default-src 'none', scripts require a per-session nonce |
| Oversized input |
Messages capped at 32,000 chars; images capped at 10 per request |
| Large file reads |
read_file rejects files over 1 MB |
Getting Started
Requirements
- VS Code
^1.85
- Node.js
^20
- An API key for OpenAI or a compatible provider
Build from source
# Clone
git clone https://github.com/AREEF3/BE-LAZY.git
cd be-lazy
# Install extension dependencies
npm install
# Install and build the webview
cd webview && npm install && npm run build && cd ..
# Compile the extension
npm run build
# Open in VS Code
code .
# Press F5 to launch the Extension Development Host
Configuration
- Click the BE LAZY icon in the Activity Bar
- Click ⚙️ to open Settings
- Select a provider (OpenAI or OmniRoute)
- Enter your API key and base URL
- Click Fetch to load available models, select one
- Click Test Connection to verify, then Save
Usage
| Action |
How |
| Send a task |
Type in the chat input and press Enter or ➤ |
| Attach an image |
Click 📎 or paste an image directly into the input |
| Stop mid-task |
Click ⏹ Stop |
| View a diff |
Click any filename in the changes panel |
| Revert a task |
Click Revert Task in the changes panel |
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feat/your-feature
- Commit your changes:
git commit -m "feat: your feature"
- Push and open a Pull Request
Please keep PRs focused — one feature or fix per PR.
License
MIT © Areef