Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

104 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

EvoCode

EvoCode is a four-layer agent-driven software engineering platform that enables autonomous code generation and system evolution through intelligent intent processing.

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Frontend Console (Port 3000)               โ”‚
โ”‚         User Interface & Intent Submission              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         Spring Boot Control Plane (Port 8080)           โ”‚
โ”‚  Orchestration, Request Routing & State Management      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚          Python AI Runtime (Port 8000)                  โ”‚
โ”‚    Intent Analysis, Code Generation & Execution        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚            Business Services Layer                      โ”‚
โ”‚  Validation, Storage, Sandboxing & Repository I/O      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Directory Structure

  • frontend/ - Next.js/TypeScript frontend application
  • control-plane/ - Spring Boot service for orchestration
  • ai-runtime/ - Python service for intent processing and code generation
  • services/ - Business service implementations and utilities
  • contracts/ - Cross-layer contract definitions (single source of truth)

See docs/WIRING.md for the as-built end-to-end request flow (auth โ†’ approval gates โ†’ SSE โ†’ persistence โ†’ ownership) and docs/RUNNING.md for running the stack.

Local Development

Docker (recommended โ€” no local toolchains needed)

If you have Docker, the whole stack builds and runs with one command โ€” no Python/JDK/Node setup required. Health-gated dependency order (ai-runtime โ†’ control-plane โ†’ frontend) and persistent volumes are built in.

cp .env.example .env            # then set EVOCODE_JWT_SECRET (e.g. `openssl rand -hex 32`)
docker compose up --build

Open http://localhost:3000 and register โ€” the first user becomes ADMIN. Data persists in named volumes (control-plane-data, ai-runtime-data) across docker compose down/up. The optional Node ts-extractor (repoPath knowledge-graph analysis) is not bundled in the runtime image; that feature degrades gracefully. See docs/RUNNING.md.

One-command start (local processes)

The whole stack (AI Runtime :8000 โ†’ Control Plane :8080 โ†’ Frontend :3000) starts in dependency order, each waited on a health check, with the JWT secret generated and injected automatically.

Windows (PowerShell):

pwsh scripts/start.ps1 setup   # first time only: install deps (venv / npm / pnpm)
pwsh scripts/start.ps1         # start all three; background processes + .logs/
pwsh scripts/start.ps1 stop    # stop everything (frees :8000/:8080/:3000)

macOS / Linux / Git Bash:

bash scripts/start.sh setup    # first time only
bash scripts/start.sh          # start all three; Ctrl-C stops everything
bash scripts/start.sh stop     # stop by port

Then open http://localhost:3000 and register. The first user to register becomes ADMIN; everyone after is a regular USER. See docs/RUNNING.md for ports, environment variables, logs, and troubleshooting.

Manual start (if you prefer running each service yourself)

Start in this order. The control plane requires EVOCODE_JWT_SECRET (a โ‰ฅ32-byte secret) โ€” it refuses to start without one (so a committed default key can't be used to forge tokens). Windows venv paths shown; on macOS/Linux use .venv/bin/....

  1. Python AI Runtime (Port 8000)

    cd ai-runtime
    python -m venv .venv
    .venv/Scripts/python -m pip install -e ".[dev]"
    .venv/Scripts/python -m uvicorn evocode_runtime.main:app --port 8000
  2. Spring Boot Control Plane (Port 8080)

    cd control-plane
    EVOCODE_JWT_SECRET="$(openssl rand -hex 32)" mvn spring-boot:run
  3. Frontend Console (Port 3000)

    cd frontend
    pnpm install
    pnpm dev

Verified End-to-End Check

All /api/** endpoints (except /api/auth/** and /actuator/health) require a JWT. Register to obtain one, then use it as a Bearer token:

# 1) Register the first user (โ†’ ADMIN) and capture the token
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"founder@example.com","password":"password123"}' \
  | python -c "import sys,json;print(json.load(sys.stdin)['token'])")

# 2) Submit an intent. The backend runs understand โ†’ plan โ†’ architect, then
#    INTERRUPTS before code generation and returns a real planned TaskGraph.
#    No files are written until you approve โ€” twice.
curl -X POST http://localhost:8080/api/intents \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"intent":"add a comments api and a product page","projectId":"shop"}'
# โ†’ {"runId":"<uuid>","status":"waiting_approval","gate":"plan",
#    "taskGraph":{"tasks":[...]},"changeSet":[],"message":"...awaiting plan approval (no files written)"}

# 3) Approve the plan โ†’ generates changeSet, stops at the diff gate (still no files on disk):
curl -X POST http://localhost:8080/api/runs/<runId>/approve -H "Authorization: Bearer $TOKEN"
# โ†’ {"status":"waiting_approval","gate":"diff","changeSet":[{"path":"evocode_generated/..."}], ...}

# 4) Approve the diff โ†’ applies to disk and completes:
curl -X POST http://localhost:8080/api/runs/<runId>/approve -H "Authorization: Bearer $TOKEN"
# โ†’ {"status":"completed","gate":null,"phase":"applied","appliedFiles":[...], ...}

curl http://localhost:8080/actuator/health   # โ†’ {"status":"UP"}  (public)
curl http://localhost:8000/health            # โ†’ {"status":"ok"}  (runtime)

For live per-node progress, use the SSE variants (POST /api/runs/stream and POST /api/runs/{id}/approve/stream) โ€” they stream phase frames per pipeline node and a terminal gate/done frame. The frontend uses these by default and falls back to the plain POST endpoints if streaming is unavailable.

Run history (persisted, owner-scoped)

The control plane persists every run, project, and session to an embedded H2 file database (control-plane/data/), surviving restarts. List/get are scoped to the current user (ADMIN sees all); non-owners get 404.

curl http://localhost:8080/api/runs -H "Authorization: Bearer $TOKEN"
# โ†’ [{"runId":"<uuid>","projectId":"shop","intent":"...","status":"completed", ...}]

curl http://localhost:8080/api/runs/<runId> -H "Authorization: Bearer $TOKEN"
# โ†’ full RunResult (taskGraph / changeSet / verification / review)

With a real project knowledge graph (optional repoPath)

Pass repoPath pointing at a React/Next.js repo. The understand step runs the Node ts-morph extractor (tools/ts-extractor/, set up once via cd tools/ts-extractor && npm ci) to build a real in-memory project graph; the Planner then plans against the actual component tree, and graphStats reports what was extracted:

curl -X POST http://localhost:8080/api/intents \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"intent":"add a product page","projectId":"shop","repoPath":"E:/evocode/test/fixtures/next-app"}'
# โ†’ {"runId":"<uuid>","status":"waiting_approval","gate":"plan",
#    "taskGraph":{"tasks":[{"kind":"frontend",...},{"kind":"test",...}]},
#    "graphStats":{"fileCount":4,"componentCount":4,"importCount":2},
#    "changeSet":[],"message":"...awaiting plan approval (no files written)"}
# Approve twice (plan gate, then diff gate) to generate and apply against the repo.

Without repoPath (or if Node/the extractor is unavailable), understand falls back to an empty placeholder graph (graphStats all zero) and planning proceeds from the intent text alone โ€” no failure.

Persistent graph cache

The extracted graph is persisted (SQLite, behind a GraphStore interface) and versioned by a repo fingerprint (file mtimes + sizes). When the same project's repo is unchanged, understand loads the stored graph instead of re-extracting โ€” graphStats.cacheHit reports which path was taken and graphVersionId identifies the stored version:

# First call for an unchanged repo โ†’ extracts and stores
# โ†’ {..., "graphStats":{"fileCount":4,...,"cacheHit":false,"graphVersionId":1}}

# Second identical call โ†’ served from the store, no re-extraction
# โ†’ {..., "graphStats":{"fileCount":4,...,"cacheHit":true,"graphVersionId":1}}

When the repo changes, the fingerprint differs, a new version is extracted and stored, and prior versions are marked superseded (never deleted). The SQLite file lives at ai-runtime/data/pkg.db (gitignored). A Postgres-backed GraphStore can replace SQLite behind the same interface without touching callers.

Impact / dependency analysis

understand runs impact/dependency analysis over the graph's IMPORTS edges (transitive reachability, both directions). graphStats.maxImpactCount reports the largest blast radius in the project โ€” the number of files transitively affected if the most-depended-on file changed โ€” and the Planner folds that signal into its task descriptions:

# โ†’ {..., "graphStats":{"fileCount":4,"componentCount":4,"importCount":2,"maxImpactCount":1}}
#   frontend task: "...๏ผˆ้กน็›ฎ็Žฐๆœ‰ 4 ไธช็ป„ไปถ๏ผ‰๏ผˆๆœ€ๅคงๅฝฑๅ“้ข 1 ๆ–‡ไปถ๏ผ‰"

The analysis distinguishes dependencies_of (what a file transitively imports) from impact_of (who transitively imports it โ€” what breaks if it changes), is cycle-safe (a file is never its own dependency), and is fully deterministic.

The pipeline runs seven nodes: understand โ†’ plan โ†’ architect โ†’ generate โ†’ verify โ†’ review โ†’ apply, with two real approval gates enforced by LangGraph interrupts: it pauses before generate (the plan gate) and before apply (the diff gate). Nothing is written to disk until the user approves both โ€” this is enforced in the backend, not simulated in the UI.

The Architect node is deterministic and graph-driven: it reads the ProjectGraph produced by understand and emits ArchitectureNotes for each planned task โ€” file locations, patterns to follow, constraints, and impact warnings. No LLM call is made here; the output is purely structural.

The Review node evaluates the generated ChangeSet and the VerificationResult, then emits a ReviewOutput with a verdict (approve, request_changes, or block), a plain-language summary, and a list of ReviewFinding items (severity, filePath, message, optional suggestedFix). The verdict is surfaced in the frontend console.

By default EvoCode runs fully offline: a deterministic stub plans tasks and templates generate file skeletons (no credentials needed). Set OPENAI_API_KEY (+ optional OPENAI_BASE_URL / OPENAI_MODEL) to switch both the plan and the code-generation stages to a real prompt-driven LLM (prompts in docs/prompts/); generation falls back to the template if the call fails, so /runs never breaks. DeepSeek is OpenAI-compatible (OPENAI_BASE_URL=https://api.deepseek.com/v1, OPENAI_MODEL=deepseek-v4-pro). A blank intent is rejected by the control plane with HTTP 400 (bean validation).

Prerequisites

  • Python 3.11 (the runtime is pinned to 3.11; use a venv as shown above)
  • JDK 21
  • Node.js 22 + pnpm 10
  • Maven 3.9
  • Git

Security

The Spring Boot control plane is the auth boundary. All /api/** endpoints require a JWT except /api/auth/** (register/login) and /actuator/health. Auth model:

  • JWT + BCrypt: stateless HS256 tokens; passwords hashed with BCrypt. The signing key comes from EVOCODE_JWT_SECRET (โ‰ฅ32 bytes) โ€” the service refuses to start without one, so no committed default key can be used to forge tokens. The one-command scripts generate and persist a per-machine secret in .evocode.env (git-ignored).
  • Ownership isolation (RBAC): every Project / Session / Run is owned by its creator. Lists are scoped to the current user; accessing another user's resource returns 404 (no existence leak). The first registered user is ADMIN (sees everything); the rest are USER.
  • Still localhost-oriented: CORS allows only http://localhost:3000, the AI runtime (:8000) itself is unauthenticated and trusts the control plane, and the H2 file DB and in-memory LangGraph checkpoints are single-node. Harden these (network policy, runtime auth, durable checkpointer, secret rotation) before any non-local deployment. See docs/RUNNING.md and docs/architecture/deployment-architecture.md.

By default the stub plans and templates generate (no credentials). Set OPENAI_API_KEY (+ optional OPENAI_BASE_URL / OPENAI_MODEL, e.g. DeepSeek) to switch both plan and code generation to a real LLM, with template fallback on failure.

Cross-Layer Contracts

All layers adhere to the contract definitions in the contracts/ directory. These schemas define:

  • IntentRequest: Structure for user intents and project context
  • RunResult: Full result of a run (status, gate, taskGraph, changeSet, verification, review)

See contracts/README.md for details on maintaining contract consistency across layers.

Contributing

When modifying contracts or adding features:

  1. Update the contract if needed in contracts/
  2. Synchronize changes to all affected layers
  3. Test cross-layer integration
  4. Document changes in relevant layer READMEs

License

EvoCode is part of the agent-driven engineering platform.

About

Agent-driven software engineering platform that transforms natural language intent into continuously evolving applications through multi-agent collaboration.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages