Skip to content

Repository files navigation


EasyCode icon

EasyCode

Practice. Prove. Remember.

Local-first algorithm training for people who want more than an accepted answer.

Write code locally, run Python tests, get context-aware AI feedback, replay your process, and return when it matters.


CILicense: MITPythonNodeFastAPIReactSQLite


WRITE → TEST → REVIEW → REPLAY → REVISIT

Quick Start · The Loop · Product Tour · Problem Bank · Configuration · 中文

EasyCode dashboard showing a local algorithm training workspace

A local workspace for turning one coding attempt into evidence you can use again.


The loop at a glance

01 · WRITE
Code in a focused local workspace.
02 · TEST
Run Python tests before you submit.
03 · REVIEW
See feedback grounded in code and process.
04 · REVISIT
Return with a concrete review date.

What EasyCode Is

EasyCode is a local algorithm-training workspace that connects coding, Python tests, AI review, process replay, and spaced repetition in one continuous workflow. It is designed to help you understand how you solved a problem—not just whether the final answer passed.

Stay in the flow
A built-in editor, timer, local drafts, and 30-second code snapshots keep the attempt together.
Leave with evidence
Python tests, complexity checks, code quality, and process history give the review something real to work from.
Ask for the next hint
Socratic guidance responds to your current approach instead of jumping straight to the solution.
Know when to return
A/B/C/D mastery ratings turn one session into a clear next review date.

Each practice session becomes a reusable training record:

  • a matching .tests.json file with Python test results
  • AI review across correctness, complexity, code quality, process, and improvements
  • history replay, Markdown export, and local share-card rendering

Quick Start

Option A: Desktop App (Windows / macOS)

Download a desktop preview from GitHub Releases:

  • macOS Apple Silicon (arm64): DMG or ZIP
  • macOS Intel (x64): DMG or ZIP
  • Windows 10/11 (x64): installer (.exe) or portable ZIP

The desktop app keeps its database, model settings, and access key in your OS user-data directory. It does not require Docker, Python, Node.js, or a network connection for local code execution. Configure an AI model in Settings if you want AI review and tutoring.

Preview signing notice: the first desktop packages are unsigned. macOS Gatekeeper and Windows SmartScreen may show a warning. Verify the downloaded file against SHA256SUMS.txt on the release page. Signed and notarized builds are the remaining requirement before the desktop release is called stable.

Option B: Docker

Use this if you want the simplest path.

git clone https://github.com/Dreamaker-TA/EasyCode.git
cd EasyCode
docker compose up --build

Open http://localhost:8000.

After the first launch, open Settings in the app and enter the model service address, model name, and access key to enable AI reviews. The key stays on your machine and is never shown in the page again.

You can also configure it before starting by creating .env in the repository root:

LLM_BASE_URL=https://api.deepseek.com
LLM_API_KEY=sk-your-real-key-here
LLM_MODEL=deepseek-v4-flash

Then restart:

docker compose up --build

Docker notes:

NeedHow
Persist progressSQLite lives in the easycode-data Docker volume
Reset everything, including model settings saved in the appdocker compose down -v
Use your own problem bankPROBLEM_BANK_HOST_PATH=/absolute/path/to/my-bank docker compose up --build
Access apphttp://localhost:8000

Option C: Source Development

Use this if you want hot reload or plan to modify the app.

Prerequisites:

ToolVersion
Python3.11+
uvlatest
Node.js20.19+
pnpm10.34.4

Bootstrap:

git clone https://github.com/Dreamaker-TA/EasyCode.git
cd EasyCode
./scripts/bootstrap.sh

The bootstrap is one command: it creates .env when missing, installs locked dependencies, imports the sample problem, updates the local database, and seeds the app. You do not need a model access key for installation.

make dev

Open http://localhost:5173. The backend runs at http://127.0.0.1:8000/api. Use the in-app Settings page to enter the model service address, model name, and access key to enable reviews, tutoring, and direct problem generation. The key stays on your machine and is never shown in the page again; you can also edit .env directly. If those ports are already taken, keep the same source workflow and choose free ports:

BACKEND_PORT=8010 FRONTEND_PORT=5174 make dev

One Problem, a Complete Training Loop

Using LeetCode 704, Binary Search, as an example, EasyCode connects local coding, Python tests, five-part AI review, process replay, and spaced review in one continuous training loop.

1. Solve locally and verify with Python tests

Write in the built-in editor while EasyCode keeps the timer, local draft, and periodic code snapshots. Run the matching Python tests before submission and view the results in the same workspace.

EasyCode problem workspace with a binary-search solution and two passing tests

2. Turn a submission into evidence-backed feedback

EasyCode combines test evidence, complexity analysis, code quality, and process history into a five-part review. The resulting A/B/C/D mastery rating sets a concrete review interval.

EasyCode AI review with an A rating, five review dimensions, and the next review date

3. Replay how the solution evolved

Step through recorded snapshots to see when key lines appeared, inspect the finished code in context, compare attempts, and export the session as Markdown.

EasyCode history replay with recorded code snapshots and a replay timeline

4. Save and share the result

Export the completed review as a share card or Markdown. The card brings the problem, mastery rating, five-dimension profile, and key diagnosis together in one image.

EasyCode share card for LeetCode 704 with an A rating and five-dimension radar chart


Build Your Own Problem Bank

Keep your bank outside this repo:

my-bank/
└─ Code/
└─ 01_basics/
├─ 01_1001_two-sum.md
├─ 01_1001_two-sum.tests.json
└─ 01_1001_two-sum.rubric.md

Point EasyCode at it:

EASYCODE_PROBLEM_BANK_ROOT=/absolute/path/to/my-bank make ingest
make dev

In Docker:

PROBLEM_BANK_HOST_PATH=/absolute/path/to/my-bank docker compose up --build

Recommended: Create with Codex or Claude Code

This repository's project skill is the recommended way to create a problem-bank entry. It turns an exercise brief into a validated Markdown problem, test file, and grading-criteria file.

Start a new agent session from the repository root, then use either:

Codex: $create-easycode-problem-bank Add a beginner array exercise to /absolute/path/to/my-bank.
Claude Code: /create-easycode-problem-bank Add a beginner array exercise to /absolute/path/to/my-bank.

The skill lives in .agents/skills/ for Codex and .claude/skills/ for Claude Code. It validates the JSON description before writing and does not overwrite existing entries.

Alternative: Create with Any AI Assistant

If you cannot use the project skill, give another AI assistant the prompt below, save its JSON response as problem.json, then run:

make problem-entry-check BANK_ROOT=/absolute/path/to/my-bank SPEC=/absolute/path/to/problem.json
make problem-entry BANK_ROOT=/absolute/path/to/my-bank SPEC=/absolute/path/to/problem.json
EASYCODE_PROBLEM_BANK_ROOT=/absolute/path/to/my-bank make ingest

The helper validates the reference Python solution against sample outputs and derives hidden-case expected_stdout from that same program.

Copy the full prompt
Create an EasyCode JSON problem description for an original programming exercise.
Output exactly one JSON object, with no Markdown fences and no extra prose.
Requirements:
- Use this directory style: Code/01_basics/01_1001_problem-title.md.
- Use "source_path" for that relative Markdown path.
- Include "id", "title", and "core".
- Use a stable numeric id whenever possible; the generated Markdown heading will follow "# <id>. <title> [★]".
- Put the public statement, examples, input/output format, and constraints in "statement_md".
- Use `###` headings inside "statement_md"; EasyCode reserves `##` for separating public and reference material.
- Put the explanation in "explanation_md".
- Include a complete runnable Python starter program in "template".
- Include a complete runnable Python reference program in "reference".
- Use checker="token" unless exact formatting or floating-point tolerance matters.
- Include at least 2 sample cases in "samples"; each sample must have "stdin", "expected", and "note".
- Include at least 3 hidden cases in "hidden"; each hidden case needs "stdin" and may include "note".
- Include 3-6 concise grading bullets in "rubric".
- Ensure the reference program really produces every sample "expected" output.

If .env already contains a working AI model configuration, make problem-generate BANK_ROOT=/absolute/path/to/my-bank is an interactive shortcut for this same AI-assisted workflow.

Manual: Write the Files Yourself

Use this format only when you prefer to maintain the Markdown and supporting files yourself.

Each problem is a Markdown file under Code/**/*.md. A matching .rubric.md file holds optional grading criteria; it is not a standalone problem.

# 1001. Two Sum ★## 题目描述
Write the public statement here: input, output, examples, and constraints.
## 解题思路
Write reference explanation here.
## Python 代码
Write a reference solution here.

Rules:

RuleMeaning
First # headingPrefer the standard LeetCode-style numeric format # <id>. <title> [★]. A trailing marks it as core.
## 题目描述Required. Files without it are not imported.
Public statementFrom ## 题目描述 to the next ## heading.
Reference materialEverything after the next ## heading. Used by review/tutor prompts, not exposed as the public statement.

Recommended title format:

# 1001. Two Sum ★

For original exercises, assign a stable numeric ID in the same style whenever possible. A plain # Problem Title ★ heading is accepted when you do not have a stable numeric ID yet, but platform-prefixed or mixed numbering styles are not recommended.

Optional Test File

Create a sibling .tests.json file to enable Python run/submit evidence:

{
"version": 1,
"time_limit_ms": 1000,
"memory_limit_mb": 128,
"checker": "token",
"cases": [
{
"id": "sample-1",
"is_sample": true,
"stdin": "2 3\n",
"expected_stdout": "5\n",
"note": "positive integers"
},
{
"id": "hidden-1",
"is_sample": false,
"stdin": "-7 4\n",
"expected_stdout": "-3\n",
"note": "negative input"
}
],
"templates": {
"python": "import sys\n\n\ndef solve(a: int, b: int) -> int:\n pass\n\n\ndef main() -> None:\n a, b = map(int, sys.stdin.read().split())\n print(solve(a, b))\n\n\nif __name__ == \"__main__\":\n main()\n"
}
}

Field summary:

FieldRequiredNotes
versionyesUse 1.
time_limit_msnoPer-case execution limit.
memory_limit_mbnoInformational limit.
checkernoUse token, exact, or float; custom is reserved and currently falls back to token behavior.
casesyesAt least one case. IDs must be unique in the file.
is_sampleyesSamples are visible in the UI; hidden cases are used for local execution evidence.
stdin / expected_stdoutyesExact program input and expected output.
templates.pythonnoStarter code for function-style practice.

Optional Grading-Criteria File

Create a sibling .rubric.md file:

- Reads exactly two integers and outputs their sum.
- Does not print extra prompt text.
- Runs in O(1) time and O(1) space.

After make ingest, these criteria guide the AI review.

For the full problem-bank specification, see PROBLEM_BANK_FORMAT.md.


Configuration

Important .env variables:

VariablePurposeDefault
LLM_BASE_URLModel service addresshttps://api.deepseek.com
LLM_API_KEYModel access keyempty
LLM_MODELModel namedeepseek-v4-flash
LLM_STRUCTURED_OUTPUTauto, json_schema, json_object, or textauto
DB_PATHSQLite database pathbackend/data/easycode.db
EASYCODE_PROBLEM_BANK_ROOTSource problem bank containing Code/examples/problem-bank unless ignored local ./Code exists
EASYCODE_PROBLEMS_JSON_PATHProblem data file created when the bank is importedbackend/data/problems.json
PROBLEM_BANK_HOST_PATHHost path to the bank that Docker can read./examples/problem-bank
CORS_ORIGINSFrontend addresses allowed during source developmentcommon frontend ports
VITE_API_BASEBackend address used by the frontend during source developmenthttp://127.0.0.1:8000/api

Provider examples:

# DeepSeek
LLM_BASE_URL=https://api.deepseek.com
LLM_API_KEY=sk-xxx
LLM_MODEL=deepseek-v4-flash
# OpenRouter
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_API_KEY=sk-or-xxx
LLM_MODEL=~anthropic/claude-sonnet-latest
# Ollama local
LLM_BASE_URL=http://localhost:11434/v1
LLM_API_KEY=ollama
LLM_MODEL=qwen2.5-coder:14b

Provider catalogs change over time. Verify model identifiers in the official DeepSeek API docs, OpenRouter model catalog, or Ollama library when configuring a different model.


Daily Commands

CommandUse
make installInstall backend and frontend dependencies
make devRun backend and frontend together
make backendRun FastAPI only
make frontendRun Vite only
make ingestRe-import the configured problem bank, update the database, then add its data
make migrateUpdate the database structure
make seedSeed DB from generated problems.json
make problem-generateInteractively generate, validate, and write one problem with the configured AI model
make problem-entry-checkPreview and validate one generated problem description without writing files
make problem-entryWrite .md, .tests.json, and .rubric.md from one description
make ciCheck the frontend code and build, then confirm the backend can run
make runtime-checkIn a temporary directory, verify problem import, database updates, saved data, and core features
make public-auditScan the publishable Git tree for blocked files, unexpected docs, private paths, and secrets
make dependency-auditScan locked frontend and backend dependencies for known vulnerabilities
make compose-checkValidate Docker Compose configuration without starting containers
make release-checkRun the complete pre-release check
make bundle-checkCheck the size of the built frontend files
make cleanRemove dependencies, local database files, and files created during problem import
make docker-updocker compose up --build
make docker-downStop containers
make docker-cleanStop containers and delete data volume

Architecture

graph LR
A["Code editor in the browser"] -->|"send requests"| B["Application service"]
A -.->|"receive review progress"| B
B --> C[("Local data")]
B --> D["AI review model"]
B --> E["Read problem bank"]
E --> F["External problem bank"]
B --> G["Review pipeline"]
B --> H["Tutor"]
B --> I["Schedule the next review"]
Loading

Core flow:

sequenceDiagram
participant U as User
participant F as Frontend
participant B as Backend
participant L as AI review model
U->>F: write Python
F->>B: upload 30s snapshots
U->>F: submit
F->>B: submit code
B-->>F: start review
B->>B: run tests when a test file exists
B->>L: review code + evidence + timeline
L-->>B: return review result
B->>B: save review and mastery, then schedule review
F->>B: get review progress
B-->>F: reviewed submission
Loading

Repository map:

backend/
app/
api/ request entry points
models/ stored data shapes
schemas/ request and response formats
services/ review, review planning, tutor, and problem-bank logic
db/ database connection
alembic/ database update history
frontend/
src/
pages/ app screens
components/ reusable interface pieces
hooks/ page state and request logic
api/ backend communication code
styles/ global styles and design variables
examples/problem-bank/ one tiny sample bank
desktop/ native launcher, backend bundling, and installer configuration
scripts/ bootstrap, ingest, generation, and release checks

Validation

cd frontend && pnpm install --frozen-lockfile
pnpm typecheck
pnpm build
cd .. && node scripts/check_frontend_bundle.mjs
make runtime-check

Before publishing, run the repeatable release gate:

make release-check

FAQ

Why is my AI review failing?

Check LLM_BASE_URL, LLM_API_KEY, and LLM_MODEL. The most common mistake is copying a key with extra backticks, quotes, or a list prefix. Values in .env should be bare literals:

LLM_API_KEY=sk-xxx

What happens if the AI model is unavailable?

The submission is preserved. Review falls back to a retryable failed state with no rating, so an unreliable review never changes your review plan.

Where is my data?

By default, SQLite lives at backend/data/easycode.db. In Docker, it lives in the easycode-data volume.


Contributing

Pull requests are welcome.

  • Keep problem banks, generated DB files, and local secrets out of the repo.
  • Run make runtime-check for behavior changes.
  • Database-structure changes need a matching Alembic update.
  • Changes to AI instructions or the review flow should also update backend/app/services/prompts/VERSION.
  • Run make release-check before opening a pull request.

License

MIT

About

Local, AI-assisted algorithm training with Python tests, process replay, Socratic tutoring, and spaced review.

Topics

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages