14 KiB
Backend Architecture
Goal
This structure keeps the Day 1 runtime stable while giving the backend a clear home for shared HTTP concerns, shared types, OpenAPI work, and feature modules. The current runtime still lives in server/index.js, but everything new should be shaped around the layout below.
Structure
server/
index.js
start.js
src/
app.ts
bootstrap.ts
config/
runtime.ts
shared/
http/
api-response.ts
async-handler.ts
error-handler.ts
not-found-handler.ts
request-context.ts
platform/
runtime-platform.ts
text.ts
stream.ts
shell.ts
path.ts
types/
app.ts
http.ts
docs/
openapi.ts
utils/
app-error.ts
logger.ts
modules/
auth/
cli-auth/
user/
settings/
projects/
files/
sessions/
git/
taskmaster/
agent/
providers/
claude/
codex/
cursor/
gemini/
mcp/
plugins/
File And Folder Roles
-
server/index.jsTemporary compatibility boundary for the old monolith. Day 1 keeps behavior here so the new TypeScript layout can grow around a stable runtime. Example: the existing websocket handlers, inline routes, and provider startup still live here until they are migrated module by module. -
server/start.jsThin production entrypoint for the compiled backend output. Example:server:startuses this file to verifyserver/dist/bootstrap.jsexists and then loads it. -
src/bootstrap.tsExecutable backend entrypoint used bynpm run serverandnpm run server:dev. Example:bootstrap.tsshould stay thin and do nothing except start the app, so later it remains safe to call in dev, prod, tests, or worker modes. -
src/app.tsComposition root for the backend application. Example: today it bridges intoindex.js; later it will create the Express app, apply shared middleware, register modules, attach websocket setup, and return the running application shape. -
src/config/Runtime configuration helpers and environment-aware path logic. Example:config/runtime.tsresolves the project root, server root, legacy runtime path, and built bootstrap path without scattering path math across the app. -
src/shared/http/Shared HTTP-level behavior that every module can reuse. Example:api-response.tsis where standard API response builders live. Example:error-handler.tsis where thrownAppErrorinstances get translated into JSON payloads. Example:request-context.tsis where request IDs, timestamps, and per-request metadata are attached. Example:async-handler.tsremoves repeatedtry/catch(next)wrappers in controllers. Example:not-found-handler.tsis the generic fallback for unknown API routes. -
src/shared/platform/Shared OS-adapter helpers for shell spawning, line ending normalization, streaming stdout/stderr parsing, and path normalization. Example:platform/stream.tsis where process output gets split into complete lines without leaking CRLF edge cases into feature code. Example:platform/shell.tsis where PowerShell-vs-bash command construction lives so provider modules do not branch onprocess.platform. -
src/shared/types/Global type aliases that are safe to share across modules. This layer usestype, notinterface. Example:types/http.tsdefinesApiMeta,ApiErrorShape,RequestContext,AuthenticatedRequest, andEndpointInventoryRecord. Example:types/app.tsdefinesRuntimePaths,AppLocals, andServerApplication. -
src/shared/docs/Shared documentation helpers and future OpenAPI registry code. Example:docs/openapi.tsis the future home for global tags likeAuth,Projects,Files,Git, andProviders, plus reusable schema registration. -
src/shared/utils/Shared non-HTTP utilities that stay generic and reusable. Example:utils/app-error.tsdefinesAppError, which feature modules can throw without knowing how HTTP serialization works. Example:utils/logger.tsis the centralized logger surface so modules do not ad-hocconsole.logeverywhere. -
src/modules/Feature boundaries. Every business area gets its own folder so request schemas, controllers, services, serializers, and docs stay close to the feature they belong to. -
src/modules/auth/Local authentication flows. Example: login, register, logout, and auth-status endpoints belong here. -
src/modules/cli-auth/CLI/provider authentication status flows for Claude, Cursor, Codex, and Gemini CLIs. Example:/api/cli/claude/statusbelongs here because it checks local CLI auth rather than app-user auth. -
src/modules/user/User-specific settings and onboarding state. Example: git identity setup and onboarding completion endpoints belong here. -
src/modules/settings/App-level stored secrets and toggles. Example: API keys and credential storage endpoints belong here because they configure backend access rather than user identity. -
src/modules/projects/Workspace and project registration concerns. Example: project listing, project creation, workspace creation, and project rename/delete flows belong here. -
src/modules/files/File tree and workspace file operations only. Example: read file, save file, upload file, create file, rename file, delete file, and image upload endpoints belong here. Example boundary rule: this module should not decide how projects are discovered; it only operates inside an already resolved project/workspace. -
src/modules/sessions/Conversation and provider session history concerns. Example: list sessions, fetch session messages, rename sessions, delete sessions, token-usage lookups, and conversation search belong here. -
src/modules/git/Repository operations and git intelligence. Example: status, diff, branch listing, checkout, commit, push, publish, discard, and AI commit-message generation endpoints belong here. -
src/modules/taskmaster/TaskMaster-specific project workflows. Example: detect installation, initialize TaskMaster, manage PRDs, add/update tasks, parse PRDs, and apply templates belong here. -
src/modules/agent/External agent execution API. Example:/api/agentbelongs here because it orchestrates provider selection, cloning, project reuse, branch creation, streaming, and optional PR creation. -
src/modules/providers/Provider-specific integrations that are narrower than the generalagentAPI. Example: provider session readers or provider-specific config endpoints should live here so Claude, Codex, Cursor, and Gemini logic do not bleed into unrelated modules. -
src/modules/providers/claude/Claude-specific runtime concerns. Example: if Claude gets module-specific schemas or adapters later, they belong here rather than inside generic session code. -
src/modules/providers/codex/Codex-specific config, session, and MCP-adjacent logic. Example: Codex MCP CLI endpoints and session history parsing can move here over time. -
src/modules/providers/cursor/Cursor-specific config, MCP, and stored session behavior. Example: Cursor config reads, MCP server mutation, and SQLite-backed session history belong here. -
src/modules/providers/gemini/Gemini-specific config and session behavior. Example: Gemini session message history and provider CLI lifecycle hooks belong here. -
src/modules/providers/mcp/MCP surfaces that are shared across providers or not owned by a single provider module. Example: generic Claude MCP CLI/config endpoints and helper endpoints belong here. -
src/modules/providers/plugins/Plugin runtime and plugin asset delivery. Example: plugin listing, installation, update, enable/disable, and asset serving can move here even though plugins are not an LLM provider; this keeps third-party integration surfaces grouped together.
Boundary Rules
app.tswires modules together; it should not contain feature logic.config/resolves environment and filesystem context; it should not know HTTP payload details.shared/http/owns transport concerns; it should not know feature rules like how to rename a project.shared/types/only contains reusable type aliases; avoid feature-specific types here unless multiple modules truly share them.modules/<feature>/owns its own futureroutes,controllers,services,schemas, anddocs.projects,files, andsessionsstay separate on purpose:projectsdecides what a workspace/project is.filesoperates inside a resolved workspace.sessionsmanages chat/session history and search.agentstays separate fromproviders:agentis orchestration for external callers.providers/*are provider-specific adapters and APIs.
Day 1 Notes
- The runtime still executes through
server/index.jsfor safety. - The new
src/structure is now the required home for all new backend code. - The generated inventory in
docs/backend/endpoint-inventory.*is the source of truth for what must be migrated into these folders next.
Package Scripts
These scripts live in package.json. The key distinction is:
serverandserver:devrun the backend directly from TypeScript.server:startruns the compiled backend throughserver/start.js.buildonly builds the frontend.server:buildonly builds the backend.startruns the full production-style flow.
Development Scripts
-
npm run devStarts the frontend and backend together. Use this for normal full-stack development when you want Vite and the API server running at the same time. Example: you are editing a React screen that calls/api/projectsand also changing the backend route behavior. -
npm run server:devStarts the backend in watch mode withtsx watch --tsconfig server/tsconfig.json server/src/bootstrap.ts. Use this for backend-only development. Example: you are refactoring request handling, logging, module structure, or shared HTTP utilities and want automatic restarts. -
npm run serverStarts the backend once from TypeScript withtsx --tsconfig server/tsconfig.json server/src/bootstrap.ts. Use this when you want a stable one-shot backend process. Example: you want to reproduce a startup bug, inspect logs without reload noise, or test one backend flow manually. -
npm run clientStarts only the Vite frontend dev server. Use this for frontend-only work when a backend is already running elsewhere. Example: you are polishing UI layout or fixing a component state bug and do not need to restart the API server.
Build And Runtime Scripts
-
npm run buildBuilds the frontend intodist/. Use this to verify production frontend bundling. Example: you changed React routing, code-splitting, or CSS and want to confirm the frontend still builds. -
npm run server:buildCompiles the backend TypeScript usingserver/tsconfig.jsonintoserver/dist/. Use this to verify backend build correctness. Example: you changedserver/src/app.ts, shared types, or future module imports and want to confirm compiled output is valid. -
npm run server:startStarts the built backend throughserver/start.js. Use this afternpm run server:buildwhen you want to run compiled backend output only. Example: dev mode works, but you want to make sure the production entrypoint and compiled files also work correctly. -
npm run startRunsnpm run build, thennpm run server:build, thennpm run server:start. Use this as the closest local equivalent to a production run. Example: before shipping, you want to confirm the built frontend and built backend work together, not just the watch-mode setup. -
npm run previewServes the built frontend bundle with Vite preview. Use this when you want to inspect the built frontend output specifically. Example: you want to check whether a client-side issue only appears in production assets. Note: this does not replace the backend server. API routes still require the backend to be running separately.
Validation Scripts
-
npm run typecheck:clientRuns TypeScript checking for the frontend only. Use this after frontend code changes. Example: you changed hook types, component props, or frontend shared models. -
npm run typecheck:serverRuns TypeScript checking for the backend only. Use this after backend code changes. Example: you changed shared HTTP helpers, backend imports, or module boundaries. -
npm run typecheckRuns both frontend and backend typechecks. Use this as the default correctness check before commit or PR. Example: you changedsrc/andserver/src/in the same branch and want one command to validate both. -
npm run lintRuns ESLint onsrc/only. Use this for frontend lint validation. Example: you changed React files and want to catch unused imports, hook issues, or style violations. -
npm run lint:fixRuns ESLint onsrc/and auto-fixes what it can. Use this after frontend edits when you want quick cleanup. Example: you renamed components and want ESLint to remove stale imports and apply automatic fixes.
Release And Lifecycle Scripts
-
npm run releaseRunsrelease.sh, which loadsGITHUB_TOKENfrom.envand then executesrelease-it. Use this only when intentionally creating a release. Example: you are cutting a new tagged version and want versioning/changelog automation. -
prepublishOnlyRuns automatically beforenpm publish. It builds both frontend and backend first. Example: this prevents publishing a broken package that was never built. -
postinstallRuns automatically afternpm install. It executesscripts/fix-node-pty.js. Example: this helps keep native terminal integration working after dependency installation. -
prepareRuns automatically during install in development contexts. It sets up Husky hooks. Example: this ensures local git hooks are installed without requiring a separate setup command.
Recommended Workflows
-
Full-stack local development:
npm installthennpm run dev -
Backend-only refactor work:
npm run server:devthennpm run typecheck:server -
Frontend-only work:
npm run client,npm run typecheck:client, andnpm run lint -
Pre-PR validation:
npm run typecheck,npm run lint,npm run build, andnpm run server:build -
Production-style local verification:
npm run start -
Release preparation:
npm run typecheck,npm run build,npm run server:build, andnpm run release