# gpt-engineer Administrator & Developer Guide

This guide is the deep reference for operators, integrators, and developers working with **gpt-engineer** — the open-source CLI that generates and improves codebases with large language models. It covers the architecture, the core library API, configuration, deployment (including Docker), security considerations, operations, and the internal pipeline in enough detail to extend or embed the tool.

It is written against gpt-engineer **0.3.1** (commit `a90fcd5`). For installation and day-to-day usage, see the companion User Guide.

---

## 1. Executive Summary

gpt-engineer is a Python application with two distinct layers:

- **A reusable core library** (`gpt_engineer/core/`) that defines a small set of abstractions — an *agent*, a *memory*, and an *execution environment* — plus a LangChain-based LLM wrapper, a robust git-diff engine, and token accounting. The core is provider-agnostic and has no CLI dependencies.
- **A CLI application** (`gpt_engineer/applications/cli/`) built on [Typer](https://typer.tiangolo.com/) that wires the core into an interactive terminal experience: argument parsing, interactive file selection, human-review feedback, and optional telemetry.

The defining behavior of gpt-engineer is that it not only *writes* code but can *execute* it: every generation produces a `run.sh` entrypoint, and the tool offers to run it. The improve workflow uses a custom unified-diff format with validation and self-correction so that LLM-produced patches apply cleanly to real files.

| Attribute | Value |
|---|---|
| Language | Python (`>=3.10,<3.13`) |
| CLI framework | Typer |
| LLM integration | LangChain (`langchain`, `langchain-openai`, `langchain-anthropic`, `langchain-community`) |
| Packaging | Poetry (`pyproject.toml`) |
| License | MIT |
| Console scripts | `gpt-engineer`, `ge`, `gpte`, `bench`, `gpte_test_application` |
| Persistent state | File-based (`.gpteng/` per project) — no database |
| Source size | 170 files / ~245 KB of source at the documented commit |

---

## 2. Architecture

### 2.1 Layered view

```text
┌──────────────────────────────────────────────────────────────┐
│  CLI application layer  —  gpt_engineer/applications/cli/      │
│                                                                │
│   main.py          Typer app: parses flags, wires the agent    │
│   cli_agent.py     CliAgent: orchestrates gen + improve         │
│   file_selector.py Interactive TOML-based file picker          │
│   learning.py      Human-review capture + consent              │
│   collect.py       RudderStack telemetry (opt-in)              │
└───────────────┬──────────────────────────────────────────────┘
                │ depends on
┌───────────────▼──────────────────────────────────────────────┐
│  Core library  —  gpt_engineer/core/                          │
│                                                                │
│   ai.py            AI: LangChain chat-model wrapper            │
│   base_agent.py        ┐                                       │
│   base_memory.py       │ abstract interfaces                   │
│   base_execution_env.py│                                       │
│   version_manager.py   ┘                                       │
│   prompt.py        Prompt value object (text + images)         │
│   files_dict.py    FilesDict: {path -> content} container      │
│   chat_to_files.py Parse LLM output into files / diffs         │
│   diff.py          Hunk/Diff model + validate & correct        │
│   token_usage.py   Tokenizer + TokenUsageLog (cost)            │
│   project_config.py gpt-engineer.toml reader/writer            │
│   linting.py       black formatting                            │
│   git.py           git helpers (stage/init/ignore)             │
│   preprompts_holder.py  loads preprompts/ templates            │
│                                                                │
│   core/default/    Concrete implementations:                   │
│     simple_agent.py     SimpleAgent (library-friendly agent)   │
│     disk_memory.py      DiskMemory  (files = key/value store)  │
│     disk_execution_env.py DiskExecutionEnv (subprocess runner) │
│     file_store.py       FileStore (writes generated files)     │
│     steps.py            gen_code / gen_entrypoint / improve_fn  │
│     paths.py / constants.py  path + tuning constants           │
└───────────────┬──────────────────────────────────────────────┘
                │ uses
┌───────────────▼──────────────────────────────────────────────┐
│  preprompts/   The system prompts that define the AI's role    │
│  tools/        custom_steps.py (clarify / lite / self-heal),   │
│                supported_languages.py                          │
│  benchmark/    `bench` CLI for evaluating agents (APPS, MBPP)  │
└────────────────────────────────────────────────────────────────┘
```

### 2.2 The three core abstractions

gpt-engineer's extensibility rests on three abstract base classes in `gpt_engineer/core/`. Any alternative implementation can be substituted without touching the rest of the system.

| Interface | File | Contract | Default implementation |
|---|---|---|---|
| `BaseAgent` | `base_agent.py` | `init(prompt) -> FilesDict`; `improve(files_dict, prompt) -> FilesDict` | `CliAgent`, `SimpleAgent` |
| `BaseMemory` | `base_memory.py` | A `MutableMapping[str \| Path, str]` — keys are filenames, values are contents | `DiskMemory` |
| `BaseExecutionEnv` | `base_execution_env.py` | `run`, `popen`, `upload(FilesDict)`, `download() -> FilesDict` | `DiskExecutionEnv` |
| `BaseVersionManager` | `version_manager.py` | `snapshot(files_dict) -> str` | (interface only) |

> [!NOTE]
> Because the agent is parameterized by *functions* for code generation, improvement, and execution (`code_gen_fn`, `improve_fn`, `process_code_fn`), the CLI can swap behaviors at runtime — for example substituting `clarified_gen`/`lite_gen` for `gen_code`, or `self_heal` for `execute_entrypoint` — without subclassing.

### 2.3 Component inventory

| Component | Responsibility |
|---|---|
| `AI` (`core/ai.py`) | Wraps a LangChain `BaseChatModel`. Selects OpenAI / Azure OpenAI / Anthropic based on model name and Azure endpoint. Handles message collapsing, serialization, streaming, vision detection, and rate-limit backoff. |
| `ClipboardAI` (`core/ai.py`) | An `AI` subclass that exchanges messages via the system clipboard instead of an API call. |
| `Prompt` (`core/prompt.py`) | Immutable value object holding prompt text, optional image URLs, and an optional entrypoint prompt. Serializes to LangChain content blocks. |
| `FilesDict` (`core/files_dict.py`) | A `dict` subclass mapping filename → content, with type enforcement and `to_chat()` / `to_log()` renderers. |
| `chat_to_files.py` | Parses LLM chat output into a `FilesDict` (fenced-code regex) and parses/apply unified diffs. |
| `diff.py` | `Hunk` and `Diff` classes; validates LLM-produced diffs against the original file and self-corrects mismatches. |
| `TokenUsageLog` (`core/token_usage.py`) | Per-step token counting via `tiktoken`, plus USD cost estimation for OpenAI models and image-token math for vision. |
| `Config` (`core/project_config.py`) | Reads/writes the optional `gpt-engineer.toml` project file. |
| `DiskMemory` (`core/default/disk_memory.py`) | File-backed key/value store; also archives prior run logs. |
| `DiskExecutionEnv` (`core/default/disk_execution_env.py`) | Runs the entrypoint as a subprocess and captures output. |
| `FileStore` (`core/default/file_store.py`) | Writes a `FilesDict` to the working directory; runs linting. |
| `steps.py` | The generation/improvement pipeline functions (see [Section 7](#7-the-generation-pipeline-internals)). |
| `custom_steps.py` (`tools/`) | Alternative pipeline functions: `clarified_gen`, `lite_gen`, `self_heal`. |
| `preprompts/` | Eight extensionless template files that compose the system prompts. |

---

## 3. Requirements & Dependencies

### 3.1 Runtime requirements

| Item | Requirement |
|---|---|
| Python | `>=3.10,<3.13` (CI/docs build on 3.11) |
| `git` | Optional but recommended; enables file staging and gitignore filtering |
| Network | Outbound HTTPS to the chosen LLM provider (unless using a local model or clipboard mode) |
| Provider key | `OPENAI_API_KEY` and/or `ANTHROPIC_API_KEY` |

### 3.2 Key Python dependencies

Declared in `pyproject.toml` under `[tool.poetry.dependencies]`. The most architecturally significant:

| Dependency | Constraint | Role in gpt-engineer |
|---|---|---|
| `openai` | `^1.0` | Underlying OpenAI client; `openai.RateLimitError` drives backoff |
| `langchain` | `>=0.1.2` | Chat-model abstraction, message schema, LLM cache |
| `langchain_openai` | `*` | `ChatOpenAI`, `AzureChatOpenAI` |
| `langchain-anthropic` | `^0.1.1` | `ChatAnthropic` for Claude models |
| `langchain-community` | `^0.2.0` | `SQLiteCache`, OpenAI cost tables |
| `typer` | `>=0.3.2` | CLI framework for `main.py` and `benchmark/__main__.py` |
| `tiktoken` | `>=0.0.4` | Token counting |
| `tomlkit` / `toml` | `^0.12.4` / `>=0.10.2` | `gpt-engineer.toml` and `file_selection.toml` |
| `regex` | `^2023.12.25` | Timeout-capable diff parsing |
| `black` | `23.3.0` | Linting/formatting of selected Python files |
| `pyperclip` | `^1.8.2` | Clipboard mode |
| `pillow` | `^10.2.0` | Image sizing for vision token math |
| `rudder-sdk-python` | `>=2.0.2` | Opt-in telemetry |
| `datasets` | `^2.17.1` | Benchmark dataset loading |

Dev and docs dependency groups (`pytest`, `mypy`, `ruff`, `pre-commit`, `tox`; and the Sphinx stack) are declared under `[tool.poetry.group.dev.dependencies]` and `[tool.poetry.group.docs.dependencies]`.

> [!IMPORTANT]
> The dependency `langchain_openai = "*"` is unpinned. When reproducing a build for production, pin it (and ideally the rest of the LangChain stack) to known-good versions — LangChain's minor releases have historically moved import paths. `token_usage.py` already guards one such move with a `try/except ImportError` around `get_openai_token_cost_for_model`.

---

## 4. Configuration Reference

gpt-engineer has three configuration surfaces: **environment variables**, the optional **`gpt-engineer.toml`** project file, and the per-project **`file_selection.toml`**.

### 4.1 Environment variables

| Variable | Default | Read in | Purpose |
|---|---|---|---|
| `OPENAI_API_KEY` | — | `main.py`, `ai.py` | OpenAI / Azure auth. Loaded from `.env` if unset. |
| `ANTHROPIC_API_KEY` | — | `main.py` | Anthropic (Claude) auth. Loaded from `.env` if unset. |
| `MODEL_NAME` | `gpt-4o` | `main.py` | Default value for `--model`. |
| `OPENAI_API_VERSION` | `2024-05-01-preview` | `ai.py` | Azure OpenAI API version. |
| `LOCAL_MODEL` | unset | `main.py` | When set, prints `$0.0` cost instead of computing one. |
| `EDITOR` | unset | `file_selector.py` | Preferred editor for interactive file selection. |
| `GPTE_TEST_MODE` | unset | `file_selector.py` | Internal: forces file selection to read from the saved TOML (used by tests). |

> [!NOTE]
> Key loading is intentionally redundant: `load_env_if_needed()` calls `load_dotenv()` and then `load_dotenv(<cwd>/.env)` for each key "for legacy reasons." The effect is that a `.env` in the current directory is always honored when the variable isn't already exported.

### 4.2 `gpt-engineer.toml` (project configuration)

An optional TOML file at the project root, parsed by `Config.from_toml()` in `core/project_config.py`. It is primarily consumed by the related `gptengineer.app` service but is read by the CLI's file selector when present. Full schema:

```toml
[run]
build = "npm run build"
test = "npm run test"
lint = "quick-lint-js"
# format = "..."             # also supported

[paths]
base = "./frontend"          # base directory to operate in (for monorepos)
src = "./src"                # source dir (under base) used as LLM context

[gptengineer-app]            # namespace for gptengineer.app
project_id = "..."           # required if this section is present
openapi = [                  # optional list of OpenAPI schemas used as context
    { url = "https://api.gptengineer.app/openapi.json" },
    { url = "https://some-color-translating-api/openapi.json" },
]
```

| Section / key | Type | Notes |
|---|---|---|
| `[run] build / test / lint / format` | string | Project commands |
| `[paths] base` | string | Base dir for monorepos |
| `[paths] src` | string | Source dir, relative to `base`, used as context |
| `[gptengineer-app] project_id` | string | **Required** when the section exists (assertion enforced) |
| `[gptengineer-app] openapi` | list of `{url}` | OpenAPI schemas provided as LLM context |

`Config.to_toml()` writes back only values that are explicitly set or differ from defaults, preserving the existing TOML document via `tomlkit`.

### 4.3 `file_selection.toml` (improve mode)

Written into `<project>/.gpteng/memory/` during improve mode. Lines marked `# "<path>" = "selected"` are deselected; uncommenting includes the file. A `[linting]` section toggles `black`:

```toml
[linting]
"linting" = "off"     # default behaviour is linting ON
```

### 4.4 Tuning constants

| Constant | Value | Location | Effect |
|---|---|---|---|
| `MAX_EDIT_REFINEMENT_STEPS` | `2` | `core/default/constants.py` | Diff-correction retry passes in improve mode |
| `MAX_SELF_HEAL_ATTEMPTS` | `10` | `tools/custom_steps.py` | Self-heal execute/fix iterations |
| `diff_timeout` | `3` (s) | CLI flag → `chat_to_files.parse_diffs` | Regex timeout for diff parsing |
| backoff | `max_tries=7, max_time=45` | `ai.backoff_inference` | Exponential backoff on `RateLimitError` |
| Claude `max_tokens_to_sample` | `4096` | `ai._create_chat_model` | Anthropic output cap |
| Vision `max_tokens` | `4096` | `ai._create_chat_model` | Vision model output cap |

---

## 5. On-Disk Layout & State

gpt-engineer keeps **all state on the filesystem** — there is no database or external store. State for a project lives under a hidden `.gpteng/` directory inside that project.

```text
<project>/
├── prompt                         # your instructions (input)
├── <generated source files>       # output
├── run.sh                         # generated entrypoint (ENTRYPOINT_FILE)
└── .gpteng/                       # META_DATA_REL_PATH
    ├── file_selection.toml        # improve-mode file picker state
    └── memory/                    # MEMORY_REL_PATH = .gpteng/memory
        └── logs/
            ├── all_output.txt          # CODE_GEN_LOG_FILE
            ├── improve.txt             # IMPROVE_LOG_FILE
            ├── diff_errors.txt         # DIFF_LOG_FILE
            ├── debug_log_file.txt      # DEBUG_LOG_FILE
            └── gen_entrypoint_chat.txt # ENTRYPOINT_LOG_FILE
```

Path constants are defined in `core/default/paths.py`:

| Constant | Value |
|---|---|
| `META_DATA_REL_PATH` | `.gpteng` |
| `MEMORY_REL_PATH` | `.gpteng/memory` |
| `ENTRYPOINT_FILE` | `run.sh` |
| `PREPROMPTS_PATH` | `<package>/preprompts` |

Other state files written outside `.gpteng/`:

| File | Location | Purpose |
|---|---|---|
| `.gpte_consent` | working directory | Stores `true` if you opted into telemetry |
| `gpt_engineer_user_id.txt` | system temp dir | Anonymous session id for telemetry |
| `.langchain.db` | working directory | SQLite LLM response cache (`--use_cache`) |
| `clipboard.txt` | working directory | Last clipboard-mode payload |

`DiskMemory.archive_logs()` rotates prior logs at the start of each run so history is preserved across invocations.

---

## 6. The AI Wrapper (`core/ai.py`)

The `AI` class is the single point of contact with language models. Understanding it is key to integrations and to supporting new providers.

### 6.1 Provider selection

`AI._create_chat_model()` chooses the LangChain chat model from the constructor arguments:

```text
if azure_endpoint set        -> AzureChatOpenAI (uses OPENAI_API_VERSION, deployment_name=model_name)
elif "claude" in model_name  -> ChatAnthropic   (max_tokens_to_sample=4096)
elif vision                  -> ChatOpenAI       (max_tokens=4096)
else                         -> ChatOpenAI
```

All variants enable streaming and attach a `StreamingStdOutCallbackHandler` so generation appears live in the terminal.

### 6.2 Vision detection

`vision` is `True` when the model name contains `vision-preview`, or contains `gpt-4-turbo` without `preview`, or contains `claude`. When the active model is non-vision, the CLI strips image URLs from the prompt before sending.

### 6.3 Message handling

- `start(system, user, *, step_name)` seeds a conversation and calls `next()`.
- `next(messages, prompt=None, *, step_name)` appends, optionally collapses consecutive same-role messages (for non-vision models), runs inference, and records token usage.
- `backoff_inference()` wraps `llm.invoke()` with `@backoff.on_exception(backoff.expo, openai.RateLimitError, max_tries=7, max_time=45)`.
- `serialize_messages()` / `deserialize_messages()` persist conversation history as JSON (normalizing LangChain's stricter chunk schema).

### 6.4 Token accounting & cost

`TokenUsageLog` (in `token_usage.py`) counts tokens per step with a `tiktoken` tokenizer (`cl100k_base` fallback for non-GPT models), accumulates totals, and can emit a CSV log. `usage_cost()` returns a USD estimate **only** for OpenAI models (`is_openai_model()` checks for `gpt` in the name); otherwise it returns `None`. Vision images are costed via `num_tokens_for_base64_image()`, which replicates OpenAI's tile-based image token formula using Pillow to measure dimensions.

At the end of a run, `main.py` prints one of:

```text
Total api cost: $  <amount>        # OpenAI model
Total api cost: $ 0.0 since we are using local LLM.   # LOCAL_MODEL set
Total tokens used:  <n>            # non-OpenAI, non-local
```

---

## 7. The Generation Pipeline (Internals)

The pipeline is a set of pure-ish functions in `core/default/steps.py` (and `tools/custom_steps.py`), composed by `CliAgent`.

### 7.1 System prompts (`preprompts/`)

The AI's behavior is defined by eight template files loaded by `PrepromptsHolder`. They are plain text with no extension:

| Preprompt | Role |
|---|---|
| `roadmap` | Frames the task: "You will get instructions for code to write… write a very long answer." |
| `generate` | Demands step-by-step reasoning, then full file output; embeds `FILE_FORMAT`. |
| `file_format` | Defines the `FILENAME` + fenced-code output contract for new code. |
| `file_format_diff` | Defines the strict unified-`git diff` contract for edits (the rules that make diffs apply cleanly). |
| `file_format_fix` | Variant used when fixing errors. |
| `improve` | Improve-mode framing; embeds `file_format_diff`. |
| `philosophy` | Coding conventions (one class per file, `requirements.txt` for Python, `package.json` for Node, pytest/dataclasses, etc.). |
| `clarify` | Drives clarify mode: ask one question or say "Nothing to clarify." |
| `entrypoint` | Instructs the model to emit terminal commands for `run.sh` (no global installs, no sudo, no placeholders). |

The generate system prompt is assembled as:

```text
roadmap + generate(FILE_FORMAT := file_format) + "\nUseful to know:\n" + philosophy
```

and the improve system prompt swaps in `improve(FILE_FORMAT := file_format_diff)`.

> [!TIP]
> `--use-custom-preprompts` copies these templates into `<project>/preprompts/` (via `get_preprompts_path`), letting a project override the AI's identity and conventions persistently. This is the supported customization seam — prefer it over forking.

### 7.2 Generate flow (`gen_code` → `gen_entrypoint` → `execute_entrypoint`)

1. **`gen_code`** sends the generate system prompt + user prompt; parses the reply with `chat_to_files_dict()` (a `(\S+)\n\s*```...```` regex) into a `FilesDict`. Logs to `all_output.txt`.
2. **`gen_entrypoint`** asks the model for a unix script that installs deps and runs the code (default instruction if no `entrypoint_prompt` is given), extracts fenced code into `run.sh`. Logs to `gen_entrypoint_chat.txt`.
3. **`execute_entrypoint`** prints the script, prompts `Do you want to execute this code? (Y/n)`, and on confirmation runs `bash run.sh` via the execution environment.

### 7.3 Improve flow (`improve_fn` → `_improve_loop` → `salvage_correct_hunks`)

1. **`improve_fn`** builds a message stack: improve system prompt + the selected files (`FilesDict.to_chat()`, which prepends line numbers) + the user prompt. Logs uploaded files to `debug_log_file.txt`.
2. **`_improve_loop`** calls the model, then `salvage_correct_hunks`, retrying up to `MAX_EDIT_REFINEMENT_STEPS` (2) times while feeding back any malformed-diff errors.
3. **`salvage_correct_hunks`** parses diffs with `parse_diffs()` (timeout-bounded `regex`), validates each non-new-file hunk against the real file via `Diff.validate_and_correct()`, then applies them with `apply_diffs()`.

### 7.4 Diff validation & correction (`core/diff.py`)

This is the most intricate part of the codebase and the reason LLM edits apply reliably. `Hunk.validate_and_correct()`:

- Locates the hunk's start line in the original file using a fuzzy `is_similar()` comparison (character-overlap ratio ≥ 0.9, whitespace/case-insensitive).
- Detects and repairs two common LLM mistakes: **skipped context lines** (re-inserts them) and **spurious extra lines** (drops them), choosing between them by comparing forward-block similarity ratios.
- Relabels misplaced comment lines as additions.
- Recomputes hunk header line numbers and offsets after correction so the resulting diff is internally consistent.

Invalid hunks that cannot be salvaged are printed (`Invalid Hunk NO.x`) and removed rather than aborting the whole patch.

### 7.5 Alternative steps (`tools/custom_steps.py`)

| Function | Substitutes for | Behavior |
|---|---|---|
| `clarified_gen` | `gen_code` | Loops on the `clarify` preprompt, gathering answers until the model says "Nothing to clarify," then generates. |
| `lite_gen` | `gen_code` | Single `ai.start()` call with the raw prompt + `file_format`; no roadmap/philosophy framing. |
| `self_heal` | `execute_entrypoint` | Runs `run.sh`; on non-zero (and non-2) exit, feeds stdout/stderr back through `improve_fn`, up to 10 times. |

---

## 8. Security

> [!CAUTION]
> gpt-engineer's core function is to **write and execute code produced by a language model**. This is powerful and inherently risky. The guidance below is essential reading before running it on any machine that holds data or credentials you care about.

### 8.1 Arbitrary code execution (by design)

`execute_entrypoint()` runs `bash run.sh` — commands authored by the LLM. There is a `Do you want to execute this code? (Y/n)` confirmation, but **self-heal mode bypasses per-attempt confirmation** and executes up to 10 times automatically. Mitigations:

- Run inside a container or disposable VM (see [Section 9](#9-deployment-docker)). The bundled Docker image isolates execution to a mounted project directory.
- Always read `run.sh` before confirming. Decline (`n`) when in doubt.
- Avoid `--self-heal` on hosts with sensitive state.

### 8.2 Path traversal in file writing

`FileStore.push()` (and `DiskMemory`) write files using paths that originate from LLM output. A community report (**issue #1346**) describes a **path-traversal** risk where LLM-generated filenames could escape the working directory and write arbitrary files. Treat the project directory as a trust boundary:

- Run generation as an unprivileged user.
- Prefer container execution where the writable mount is scoped to `/project`.
- Review generated file paths, not just file contents, before execution.

> [!WARNING]
> This behavior is not unique to one mode — any flow that writes a `FilesDict` to disk relies on the LLM's filenames. Containerization is the most robust mitigation.

### 8.3 Telemetry & data handling

`applications/cli/collect.py` can send "learning" data (prompt, model, config, logs, and your review answers) to a RudderStack endpoint to help improve the tool. Important facts for operators:

- It is **opt-in**. `check_collection_consent()` only sends data if `.gpte_consent` contains `true`; otherwise the user is prompted.
- The RudderStack `write_key` and `dataPlaneUrl` are **hardcoded** in `collect.py`.
- Disable by answering `n`, or by deleting `.gpte_consent`. For fleet deployments, pre-create `.gpte_consent` absent/empty, or strip `collect.py` calls.
- Payloads are truncated to RudderStack's 32 KB event limit (`collect_learnings` trims oversized logs).

> [!IMPORTANT]
> In regulated or air-gapped environments, audit and disable telemetry explicitly. Logs sent can include prompt text and session logs.

### 8.4 Secrets

API keys are read from the environment or a `.env` file. The `.env.template` ships placeholder values only. Standard hygiene applies: keep `.env` out of version control, scope keys narrowly, and prefer short-lived keys for shared runners.

### 8.5 Docker permission note

`docker/entrypoint.sh` runs `chown -R nobody:nogroup` and `chmod -R 777` on generated files (except the `prompt` file) so host users can access container output. World-writable output is convenient but loose — tighten it if the host is multi-tenant.

---

## 9. Deployment (Docker)

Running gpt-engineer in Docker is the recommended way to contain code execution.

### 9.1 Image

`docker/Dockerfile` is a two-stage build on `python:3.11-slim`:

- **Builder stage** installs `tk`, `tcl`, `curl`, `git`, then `pip install --no-cache-dir -e .`.
- **Final stage** copies site-packages and binaries from the builder, copies the app, and sets `ENTRYPOINT ["bash", "/app/entrypoint.sh"]`.

`docker/entrypoint.sh` invokes `gpt-engineer /project "$@"` against a mounted `/project` directory, then fixes output permissions.

### 9.2 Build & run with the Docker CLI

```bash
docker build --rm -t gpt-engineer -f docker/Dockerfile .

docker run -it --rm \
  -e OPENAI_API_KEY="YOUR_OPENAI_KEY" \
  -v ./your-project:/project \
  gpt-engineer
```

```text
Running gpt-engineer in /project
...
Do you want to execute this code? (Y/n)
```

The mounted directory must contain a `prompt` file. Any flags after the image name are passed straight through to `gpt-engineer`.

### 9.3 Docker Compose

`docker-compose.yml` defines a single `gpt-engineer` service with `stdin_open`/`tty` for interactivity, an `env_file: .env`, and a volume mapping `./projects/example:/project`:

```bash
docker compose -f docker-compose.yml build
docker compose run --rm gpt-engineer
```

Edit the `volumes` entry to point at your own project, and provide the API key via `.env` or an `environment:` override.

### 9.4 Debugging the image

```bash
docker run -it --entrypoint /bin/bash gpt-engineer
```

This opens a shell inside the container so you can inspect the environment manually.

---

## 10. Operations

### 10.1 Running locally (Makefile)

The `Makefile` provides three convenience targets:

| Target | Action |
|---|---|
| `make help` | Print usage |
| `make install` | `poetry install` + install pre-commit hooks + farewell message |
| `make run <name>` | `poetry run gpt-engineer projects/<name>` |
| `make cloc` | Count lines of code (excludes vendored/benchmark dirs) |

### 10.2 Logs & diagnostics

Per-project logs live in `<project>/.gpteng/memory/logs/`. The most useful for incident triage:

- `debug_log_file.txt` — uploaded files, prompt, and the full console capture from improve mode (including stack traces).
- `diff_errors.txt` — per-hunk validation problems from the diff engine.
- `all_output.txt` / `improve.txt` — full step transcripts.

`gpte --sysinfo` prints OS, OS version, architecture, Python version, and the full `pip list`, then exits — attach this to bug reports.

### 10.3 Cost & token control

- `--use_cache` enables `SQLiteCache(.langchain.db)` so identical requests are free on rerun.
- `--lite` reduces prompt overhead for small tasks.
- Token totals and (for OpenAI) USD cost print at the end of each run; the per-step CSV is available via `TokenUsageLog.format_log()`.

### 10.4 Rate limits

Inference is wrapped in exponential backoff (`max_tries=7`, `max_time=45 s`) on `openai.RateLimitError`. Persistent throttling beyond that window surfaces as a `RateLimitError` to the user.

---

## 11. Benchmarking (`bench`)

gpt-engineer installs a separate `bench` console script (`gpt_engineer/benchmark/__main__.py`, a Typer app) for evaluating agent implementations against public datasets.

```text
bench PATH_TO_AGENT [BENCH_CONFIG] [--yaml-output FILE] [--verbose]
```

| Argument / option | Default | Purpose |
|---|---|---|
| `PATH_TO_AGENT` | (required) | A Python file exposing a `default_config_agent()` function |
| `BENCH_CONFIG` | `benchmark/default_bench_config.toml` | Selects which benchmark tasks to run |
| `--yaml-output` | none | Write per-task results to a YAML file |
| `--verbose` | off | Verbose output |

Currently supported benchmarks are **APPS** and **MBPP** (loaded via the `datasets` dependency). The agent under test is imported dynamically with `importlib`. A starter agent template is available in the project's `gpte-bench-template` repository.

> [!NOTE]
> `bench` dynamically imports and executes the agent module you point it at. Only run it against agent code you trust.

---

## 12. Code Quality & Conventions

| Aspect | Detail |
|---|---|
| Formatter | `black` (pinned `23.3.0`), config in `[tool.black]` (`target-version = py310`) |
| Linter | `ruff` (`select = ["F","E","W","I001"]`, ignores `E501`, `E722`) |
| Type checking | `mypy 1.3.0` |
| Pre-commit | `black`, `ruff --fix`, plus `check-toml`, `check-yaml`, `detect-private-key`, EOF/whitespace fixers; `fail_fast: true` |
| Tests | `pytest`; marker `requires_key` flags tests needing a real `OPENAI_API_KEY` (deselect with `-m "not requires_key"`) |
| Docstrings | NumPy-style throughout; enforced culturally (see `sweep.yaml` rules) |
| Docs | Sphinx on Read the Docs (`.readthedocs.yaml`, Python 3.11, `poetry install --with docs`, `docs/create_api_rst.py` pre-build) |

The codebase has good test coverage around the fragile parts: `tests/core/` exercises the diff engine (`test_salvage_correct_hunks.py`), `chat_to_files`, token usage, and the disk-backed memory/execution environments; `tests/applications/cli/` covers the CLI agent, file collection, consent, and the main entrypoint across all modes.

---

## 13. Extending gpt-engineer

Common extension points, in order of how cleanly they're supported:

1. **Override the system prompts.** Use `--use-custom-preprompts` (or ship a `preprompts/` folder) to change conventions, language defaults, or the AI's role — no code changes.
2. **Swap step functions.** Provide your own `code_gen_fn`, `improve_fn`, or `process_code_fn` to `CliAgent`/`SimpleAgent`. This is how `clarify`/`lite`/`self-heal` are implemented.
3. **Implement a `BaseExecutionEnv`.** Replace `DiskExecutionEnv` to run generated code in a sandbox, remote host, or container instead of a local subprocess. The interface (`run`, `popen`, `upload`, `download`) is deliberately small.
4. **Implement a `BaseMemory`.** Replace `DiskMemory` to store project state somewhere other than the local filesystem.
5. **Add a provider.** Extend `AI._create_chat_model()` with a new branch returning the appropriate LangChain chat model, and update `vision`/`is_openai_model` heuristics as needed.

> [!TIP]
> Embedding gpt-engineer as a library is best done through `SimpleAgent` (`core/default/simple_agent.py`) rather than the CLI's `CliAgent`, since `SimpleAgent` has no CLI/telemetry/file-selection coupling.

---

## 14. Potential Improvements & Tech Debt

Observations drawn directly from the source at this commit:

- **Unpinned `langchain_openai = "*"`.** A floating major dependency in a project whose token-cost code already works around one LangChain import move. Pin the LangChain stack for reproducible builds.
- **Path-traversal exposure (issue #1346).** File writes trust LLM-supplied paths. A sanitizing/normalization layer in `FileStore.push()` would harden the default (non-Docker) path.
- **Hardcoded telemetry credentials.** The RudderStack `write_key` lives in source. A config/env toggle (or a build flag to compile it out) would suit enterprise deployments better than the current opt-in file.
- **Provider/vision detection by substring.** Model routing keys off substrings like `"claude"`, `"gpt-4-turbo"`, and `"vision-preview"`. New model names (e.g., OpenRouter routing, Gemini, Ollama — issues #1321, #1319) don't fit cleanly; a provider registry would scale better.
- **`run.sh`-centric execution.** The pipeline assumes a unix `bash` entrypoint; Windows execution leans on WSL/Docker. A pluggable entrypoint strategy would broaden native support.
- **World-writable Docker output** (`chmod -R 777` in `entrypoint.sh`) trades safety for convenience.

---

## 15. Quick Reference

### Console scripts (from `pyproject.toml`)

| Command | Target | Purpose |
|---|---|---|
| `gpt-engineer`, `ge`, `gpte` | `applications.cli.main:app` | Main CLI |
| `bench` | `benchmark.__main__:app` | Agent benchmarking |
| `gpte_test_application` | `tests.caching_main:app` | Test harness (internal) |

### Key paths

| Path | Meaning |
|---|---|
| `<project>/prompt` | User instructions (input) |
| `<project>/run.sh` | Generated entrypoint |
| `<project>/.gpteng/memory/logs/` | Step transcripts |
| `<project>/.gpteng/memory/file_selection.toml` | Improve-mode selection |
| `.gpte_consent` | Telemetry opt-in flag |
| `.langchain.db` | LLM response cache |

### Tuning knobs

| Knob | Default | Where |
|---|---|---|
| `--diff_timeout` | 3 s | CLI |
| `MAX_EDIT_REFINEMENT_STEPS` | 2 | `constants.py` |
| `MAX_SELF_HEAL_ATTEMPTS` | 10 | `custom_steps.py` |
| backoff | 7 tries / 45 s | `ai.py` |

### Environment variables

`OPENAI_API_KEY` · `ANTHROPIC_API_KEY` · `MODEL_NAME` · `OPENAI_API_VERSION` · `LOCAL_MODEL` · `EDITOR` · `GPTE_TEST_MODE`

---

_Generated June 2026 from AntonOsika/gpt-engineer at a90fcd5. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
