Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,25 +259,20 @@ Our aim is to make the developer setup as straightforward as possible. If you en

## Quick Start 🚀

### Local Development

```bash
git clone https://github.com/eigent-ai/eigent.git
cd eigent
npm install
npm run dev

# In a separate terminal, start the backend server
cd server
docker compose up -d
# Stream the logs if you needed
docker compose logs -f
```

To run the application locally in developer mode:
Then go to settings to specify your model key and model type.

### CLI

1. Configure `.env.development`:
- Set `VITE_USE_LOCAL_PROXY=true`
- Set `VITE_PROXY_URL=http://localhost:3001`
1. Go to the settings to specify your model key and model type.
See [cli/README.md](cli/README.md) for CLI installation and usage.

## Common Actions 🔄

Expand Down
38 changes: 26 additions & 12 deletions backend/app/controller/chat_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ async def post(data: Chat, request: Request):

os.environ["file_save_path"] = data.file_save_path()
os.environ["browser_port"] = str(data.browser_port)

# Store workspace on task_lock so follow-up tasks preserve it
if data.workspace:
task_lock.workspace = data.workspace
os.environ["OPENAI_API_KEY"] = data.api_key
os.environ["OPENAI_API_BASE_URL"] = (
data.api_url or "https://api.openai.com/v1"
Expand Down Expand Up @@ -290,18 +294,28 @@ def improve(id: str, data: SupplementChat):
# Get current environment values needed to construct new path
current_email = None

# Extract email from current file_save_path if available
current_file_save_path = os.environ.get("file_save_path", "")
if current_file_save_path:
path_parts = Path(current_file_save_path).parts
if len(path_parts) >= 3 and "eigent" in path_parts:
eigent_index = path_parts.index("eigent")
if eigent_index + 1 < len(path_parts):
current_email = path_parts[eigent_index + 1]

# If we have the necessary info, update
# the file_save_path
if current_email and id:
# If using a custom workspace, keep it as-is
if hasattr(task_lock, "workspace") and task_lock.workspace:
new_folder_path = Path(task_lock.workspace)
new_folder_path.mkdir(parents=True, exist_ok=True)
task_lock.new_folder_path = new_folder_path
os.environ["file_save_path"] = str(new_folder_path)
current_email = "__workspace__"
else:
# Extract email from current file_save_path if available
current_file_save_path = os.environ.get("file_save_path", "")
if current_file_save_path:
path_parts = Path(current_file_save_path).parts
if len(path_parts) >= 3 and "eigent" in path_parts:
eigent_index = path_parts.index("eigent")
if eigent_index + 1 < len(path_parts):
current_email = path_parts[eigent_index + 1]

# Only rewrite file_save_path for web-app users (real email).
# CLI (dev mode only) sets email to "__workspace__" so files
# stay in the local workspace directory instead of the
# ~/eigent/{email}/... structure used by the web app.
if current_email and current_email != "__workspace__" and id:
# Create new path using the existing
# pattern: email/project_{id}/task_{id}
new_folder_path = (
Expand Down
28 changes: 17 additions & 11 deletions backend/app/model/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class Chat(BaseModel):
search_config: dict[str, str] | None = None
# User identifier for user-specific skill configurations
user_id: str | None = None
# Optional workspace directory — when set, agents work in this
# directory instead of the default ~/eigent/<email>/... path
workspace: str | None = None

@field_validator("model_type")
@classmethod
Expand Down Expand Up @@ -119,17 +122,20 @@ def is_cloud(self):
return self.api_url is not None and "44.247.171.124" in self.api_url

def file_save_path(self, path: str | None = None):
email = re.sub(r'[\\/*?:"<>|\s]', "_", self.email.split("@")[0]).strip(
"."
)
# Use project-based structure: project_{project_id}/task_{task_id}
save_path = (
Path.home()
/ "eigent"
/ email
/ f"project_{self.project_id}"
/ f"task_{self.task_id}"
)
if self.workspace:
save_path = Path(self.workspace)
else:
email = re.sub(
r'[\\/*?:"<>|\s]', "_", self.email.split("@")[0]
).strip(".")
# Use project-based structure: project_{project_id}/task_{task_id}
save_path = (
Path.home()
/ "eigent"
/ email
/ f"project_{self.project_id}"
/ f"task_{self.task_id}"
)
if path is not None:
save_path = save_path / path
save_path.mkdir(parents=True, exist_ok=True)
Expand Down
7 changes: 7 additions & 0 deletions cli/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
EIGENT_API_URL=http://localhost:5001
EIGENT_API_KEY=sk-...
EIGENT_MODEL_PLATFORM=openai
EIGENT_MODEL_TYPE=gpt-4o
EIGENT_EMAIL=cli@eigent.ai
# Optional: custom OpenAI-compatible endpoint
# EIGENT_API_ENDPOINT=https://api.openai.com/v1
99 changes: 99 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Eigent CLI

Command-line interface for the Eigent AI coding agent.

## Prerequisites

- Node.js >= 18
- Eigent backend running (default: `http://localhost:5001`)

## Installation

```bash
# From the repo root
cd cli
npm install
npm run build
npm install -g .
```

After installation, the `eigent` command is available globally.

## Configuration

Run the config wizard to set your API key, model, and backend URL:

```bash
eigent config
```

Config is stored at `~/.eigent/cli-config.json`.

You can also use environment variables (or `.env.development` at the repo root):

```
EIGENT_API_URL=http://localhost:5001
EIGENT_API_KEY=sk-...
EIGENT_MODEL_PLATFORM=openai
EIGENT_MODEL_TYPE=gpt-4o
EIGENT_EMAIL=you@example.com
```

## Usage

### Interactive mode (REPL)

```bash
eigent
```

### One-shot mode

```bash
eigent "search for YC W2026 companies and save one to w26.json"
```

### CLI options

```
eigent [question] Ask a question (one-shot mode)
eigent config Configure API key, model, backend URL
eigent --help Show help
eigent --version Show version
```

### REPL commands

| Command | Description |
|----------------------|--------------------------------------|
| `/new` | Start a new conversation |
| `/project` | Show current project ID |
| `/workspace` | Show current workspace path |
| `/workspace <path>` | Change workspace directory |
| `/stop` | Stop the current task |
| `/quit` or `/exit` | Exit the CLI |
| `Ctrl+C` | Stop streaming / exit |
| `Ctrl+E` | Expand last collapsed output |

## Development

```bash
# Watch mode (auto-rebuild on changes)
npm run dev

# Manual build
npm run build

# Run without global install
node dist/index.js
```

## Updating

After pulling changes:

```bash
cd cli
npm run build
npm install -g .
```
30 changes: 30 additions & 0 deletions cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@eigent/cli",
"version": "0.1.0",
"description": "Eigent AI coding agent CLI",
"type": "module",
"bin": {
"eigent": "./dist/index.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js"
},
"dependencies": {
"chalk": "^5.4.1",
"commander": "^13.1.0",
"dotenv": "^17.3.1",
"marked": "^15.0.7",
"marked-terminal": "^7.3.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"@types/node": "^22.15.0",
"@types/uuid": "^10.0.0",
"typescript": "^5.7.0"
},
"engines": {
"node": ">=18.0.0"
}
}
Loading
Loading