diff --git a/README.md b/README.md
index a66e8c0..dffd677 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,28 @@
# E2B Code Execution Agent
-An advanced Mastra template that provides a coding agent capable of planning, writing, executing, and iterating on code in secure, isolated E2B sandboxes with comprehensive file management and development workflow capabilities.
+This repository is a production-ready Mastra template for running your own fully personalized coding copilot. It packages the AI agent, long-term memory, custom tooling, and an Electron desktop shell so you can interact with the assistant on any machine without wiring the pieces together yourself.
+
+## What You Get
+
+- **AI Coding Agent** – A Mastra-powered assistant that can plan, write, run, and iterate on code in secure E2B sandboxes without touching your local filesystem.
+- **Long-Term Memory** – Persistent summaries, semantic recall, and working-memory threads so the agent remembers what you are building.
+- **Custom Workflow Commands** – A catalog of reusable shell commands you define (build, test, deploy) that the agent can execute with a single instruction.
+- **Desktop App** – An Electron launcher that starts/stops the agent, streams logs, and stores your API keys so you can run the assistant without a terminal.
+- **Environment Doctor** – A diagnostic script that verifies Node.js, installed dependencies, and required API keys before the agent launches.
+
+These pieces are wired together out of the box—you only need to add your API keys and optional personalization details.
## Overview
This template demonstrates how to build an AI coding assistant that can work with real development environments. The agent can create sandboxes, manage files and directories, execute code in multiple languages, and monitor development workflows - all within secure, isolated E2B environments.
+### Typical Use Cases
+
+- Spin up a fresh sandbox to prototype features without polluting local projects.
+- Ask the agent to scaffold applications, write tests, or refactor code across files.
+- Run your custom automation (e.g., `pnpm lint`, deployment scripts) from the agent via the `runCustomCommand` tool.
+- Rely on the persistent memory so the agent tracks progress across sessions and remembers your preferences.
+
## Features
- **Secure Code Execution**: Run Python, JavaScript, and TypeScript code in isolated E2B sandboxes
@@ -14,6 +31,8 @@ This template demonstrates how to build an AI coding assistant that can work wit
- **Live Development Monitoring**: Watch directory changes and monitor development workflows
- **Command Execution**: Run shell commands, install packages, and manage dependencies
- **Memory System**: Persistent conversation memory with semantic recall and working memory
+- **Personalization Layer**: Configure agent identity, preferences, and a catalog of repeatable workflows
+- **Desktop Launcher**: Electron-based desktop application for starting/stopping the agent without the terminal
- **Development Workflows**: Professional development patterns with build automation
## Prerequisites
@@ -22,14 +41,17 @@ This template demonstrates how to build an AI coding assistant that can work wit
- E2B API key (sign up at [e2b.dev](https://e2b.dev))
- OpenAI API key
-## Setup
+## Run It Locally
+
+Follow these steps the first time you set up the project on a new machine.
1. **Clone and install dependencies:**
```bash
git clone https://github.com/mastra-ai/template-coding-agent.git
cd template-coding-agent
- pnpm install
+ npm install
+ # or: pnpm install
```
2. **Set up environment variables:**
@@ -44,12 +66,31 @@ This template demonstrates how to build an AI coding assistant that can work wit
OPENAI_API_KEY="your-openai-api-key-here"
```
-3. **Start the development server:**
+3. **Check your environment:**
```bash
- pnpm run dev
+ npm run doctor
```
+ The doctor script highlights missing dependencies or API keys before you launch the agent.
+
+4. **Personalize the agent (optional but recommended):**
+
+ Open `src/mastra/agents/personal-config.ts` and update the identity, mission, preferences, memory strategy, and custom command catalog so the assistant behaves the way you want.
+
+5. **Start the agent (pick one):**
+
+ - **CLI:** `npm run dev` for hot-reload development or `npm run start` for production mode.
+ - **Desktop App:** `npm run desktop` to open the Electron launcher. Provide your API keys once and start/stop the agent with buttons. Output streaming and status updates are surfaced in the UI.
+
+6. **Package a desktop build (optional):**
+
+ ```bash
+ npm run desktop:package
+ ```
+
+ This produces a platform-specific build in the `dist/` directory using `electron-builder`.
+
## Architecture
### Core Components
@@ -63,6 +104,7 @@ The main agent with comprehensive development capabilities:
- **File Operations**: Complete CRUD operations for files and directories
- **Development Monitoring**: Watches for changes and monitors workflows
- **Memory Integration**: Maintains conversation context and project history
+- **Personal Profile**: Dynamically incorporates preferences defined in `personal-config.ts`
#### **E2B Tools** (`src/mastra/tools/e2b.ts`)
@@ -98,6 +140,7 @@ Complete toolkit for sandbox interaction:
**Development Workflow:**
- `runCommand` - Execute shell commands, build scripts, package management
+- `runCustomCommand` - Trigger user-defined workflows stored in `personal-config.ts`
### Memory System
@@ -117,22 +160,56 @@ E2B_API_KEY=your_e2b_api_key_here
OPENAI_API_KEY=your_openai_api_key_here
```
-### Customization
+### Personalization & Custom Commands
-You can customize the agent behavior by modifying the instructions in `src/mastra/agents/coding-agent.ts`:
+Fine-tune the assistant without editing core agent code by updating `src/mastra/agents/personal-config.ts`.
```typescript
-export const codingAgent = new Agent({
- name: 'Coding Agent',
- instructions: `
- // Customize agent instructions here
- // Focus on specific languages, frameworks, or development patterns
- `,
- model: openai('gpt-4.1'),
- // ... other configuration
-});
+export const personalAgentConfig = {
+ name: 'My Personal Coding Companion',
+ tagline: 'A persistent AI partner tailored to your workflows.',
+ mission: 'Describe the long-term goals for the agent.',
+ preferences: {
+ languages: ['TypeScript', 'Python'],
+ technologies: ['Node.js', 'React'],
+ developmentStyle: ['Prefer TDD', 'Document architectural decisions'],
+ },
+ memoryStrategy: {
+ longTermSummary: 'What should be remembered across sessions.',
+ workingMemoryGuidelines: ['Snapshot progress at the end of each session.'],
+ },
+ customCommands: [
+ {
+ id: 'start-dev',
+ label: 'Start Dev Server',
+ description: 'Launch the development server inside the sandbox.',
+ command: 'pnpm run dev',
+ },
+ ],
+};
```
+Key capabilities provided by the configuration file:
+
+- **Agent identity** — control tone, mission, and project focus.
+- **Preference sets** — inform the agent about favored languages, tools, and delivery style.
+- **Memory strategy** — guide how long-term and short-term context should be stored.
+- **Custom commands** — register reusable shell commands that can be triggered with the `runCustomCommand` tool using a `commandId`.
+
+Feel free to extend the configuration with additional custom commands or memory options; the agent automatically reads the updated details on startup.
+
+### Desktop Launcher
+
+The Electron desktop app wraps the same Mastra agent so you can run it without the terminal:
+
+1. Install dependencies and run `npm run doctor` to verify prerequisites.
+2. Launch the desktop shell with `npm run desktop`.
+3. Enter your API keys once—the app stores them locally using the operating system's application storage.
+4. Start or stop the agent with a single click. Live logs and status updates stream into the UI so you can monitor the session in real time.
+5. Package a distributable build with `npm run desktop:package` when you are ready to share it across machines.
+
+The launcher proxies your chosen mode (`mastra dev` for development or `mastra start` for production) and forwards environment variables so the agent runs exactly as it does via the CLI.
+
## Common Issues
### "E2B_API_KEY is not set"
@@ -171,8 +248,10 @@ export const codingAgent = new Agent({
src/mastra/
agents/
coding-agent.ts # Main coding agent with development capabilities
+ personal-config.ts # User-editable persona, memory, and command catalog
tools/
e2b.ts # Complete E2B sandbox interaction toolkit
+ custom-commands.ts # Tool for invoking reusable workflows
index.ts # Mastra configuration with storage and logging
```
diff --git a/desktop/electron-main.mjs b/desktop/electron-main.mjs
new file mode 100644
index 0000000..1624584
--- /dev/null
+++ b/desktop/electron-main.mjs
@@ -0,0 +1,180 @@
+import { app, BrowserWindow, ipcMain } from 'electron';
+import { spawn } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+import path from 'node:path';
+import process from 'node:process';
+import { existsSync } from 'node:fs';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const projectRoot = path.resolve(__dirname, '..');
+
+let mainWindow;
+let agentProcess = null;
+let currentMode = null;
+
+const sendStatus = status => {
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ mainWindow.webContents.send('agent:status', status);
+ }
+};
+
+const sendOutput = payload => {
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ mainWindow.webContents.send('agent:output', payload);
+ }
+};
+
+const resolveRendererPath = () => {
+ const rendererUrl = new URL('./renderer/index.html', import.meta.url);
+ return fileURLToPath(rendererUrl);
+};
+
+const resolvePreloadPath = () => {
+ const preloadUrl = new URL('./preload.mjs', import.meta.url);
+ return fileURLToPath(preloadUrl);
+};
+
+const createWindow = () => {
+ mainWindow = new BrowserWindow({
+ width: 1100,
+ height: 720,
+ title: 'Coding Agent Desktop',
+ webPreferences: {
+ preload: resolvePreloadPath(),
+ },
+ });
+
+ mainWindow.loadFile(resolveRendererPath());
+
+ mainWindow.on('closed', () => {
+ mainWindow = null;
+ });
+};
+
+const startAgentProcess = (mode, extraEnv) => {
+ if (agentProcess) {
+ return { ok: false, message: 'The agent is already running.' };
+ }
+
+ const mastraBinary = path.join(
+ projectRoot,
+ 'node_modules',
+ '.bin',
+ process.platform === 'win32' ? 'mastra.cmd' : 'mastra',
+ );
+
+ if (!existsSync(mastraBinary)) {
+ return {
+ ok: false,
+ message:
+ 'Could not locate the Mastra CLI. Install dependencies first with "npm install" or "pnpm install".',
+ };
+ }
+
+ const commandArgs = mode === 'dev' ? ['dev'] : ['start'];
+
+ const spawnOptions = {
+ cwd: projectRoot,
+ env: { ...process.env, ...extraEnv },
+ shell: process.platform === 'win32',
+ };
+
+ try {
+ agentProcess = spawn(mastraBinary, commandArgs, spawnOptions);
+ } catch (error) {
+ return { ok: false, message: `Failed to launch Mastra: ${error.message}` };
+ }
+
+ currentMode = mode;
+ sendStatus({ state: 'running', mode });
+
+ const forward = (type, data) => {
+ sendOutput({ type, data: data.toString(), timestamp: Date.now() });
+ };
+
+ agentProcess.stdout?.on('data', chunk => forward('stdout', chunk));
+ agentProcess.stderr?.on('data', chunk => forward('stderr', chunk));
+
+ agentProcess.on('exit', code => {
+ sendOutput({ type: 'event', data: `Agent exited with code ${code ?? 'unknown'}`, timestamp: Date.now() });
+ currentMode = null;
+ sendStatus({ state: 'stopped' });
+ agentProcess = null;
+ });
+
+ agentProcess.on('error', error => {
+ sendOutput({ type: 'error', data: error.message, timestamp: Date.now() });
+ currentMode = null;
+ sendStatus({ state: 'stopped' });
+ agentProcess = null;
+ });
+
+ return { ok: true };
+};
+
+const stopAgentProcess = () => {
+ if (!agentProcess) {
+ return { ok: false, message: 'The agent is not running.' };
+ }
+
+ sendStatus({ state: 'stopping', mode: currentMode });
+
+ if (process.platform === 'win32') {
+ agentProcess.kill();
+ } else {
+ agentProcess.kill('SIGINT');
+ setTimeout(() => agentProcess?.kill('SIGKILL'), 4000);
+ }
+
+ return { ok: true };
+};
+
+app.whenReady().then(() => {
+ createWindow();
+
+ app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow();
+ }
+ });
+});
+
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') {
+ app.quit();
+ }
+});
+
+app.on('before-quit', () => {
+ if (agentProcess) {
+ stopAgentProcess();
+ }
+});
+
+ipcMain.handle('agent:start', async (_event, payload) => {
+ const { mode, env } = payload ?? {};
+ if (!mode || (mode !== 'dev' && mode !== 'start')) {
+ return { ok: false, message: 'Invalid mode. Choose either "dev" or "start".' };
+ }
+
+ const result = startAgentProcess(mode, env ?? {});
+ if (!result.ok) {
+ sendStatus({ state: 'stopped' });
+ }
+ return result;
+});
+
+ipcMain.handle('agent:stop', async () => {
+ const result = stopAgentProcess();
+ if (!result.ok) {
+ return result;
+ }
+ return { ok: true };
+});
+
+ipcMain.handle('agent:status', async () => {
+ if (!agentProcess) {
+ return { state: 'stopped' };
+ }
+ return { state: 'running', mode: currentMode };
+});
diff --git a/desktop/preload.mjs b/desktop/preload.mjs
new file mode 100644
index 0000000..8dd274b
--- /dev/null
+++ b/desktop/preload.mjs
@@ -0,0 +1,17 @@
+import { contextBridge, ipcRenderer } from 'electron';
+
+contextBridge.exposeInMainWorld('codingAgentDesktop', {
+ startAgent: options => ipcRenderer.invoke('agent:start', options),
+ stopAgent: () => ipcRenderer.invoke('agent:stop'),
+ getStatus: () => ipcRenderer.invoke('agent:status'),
+ onOutput: callback => {
+ const listener = (_event, payload) => callback(payload);
+ ipcRenderer.on('agent:output', listener);
+ return () => ipcRenderer.removeListener('agent:output', listener);
+ },
+ onStatus: callback => {
+ const listener = (_event, payload) => callback(payload);
+ ipcRenderer.on('agent:status', listener);
+ return () => ipcRenderer.removeListener('agent:status', listener);
+ },
+});
diff --git a/desktop/renderer/index.html b/desktop/renderer/index.html
new file mode 100644
index 0000000..c0277a0
--- /dev/null
+++ b/desktop/renderer/index.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+ Coding Agent Desktop
+
+
+
+
+
Personal Coding Agent
+
Launch, monitor, and manage your personalized Mastra coding agent without the terminal.