Graduation Project β Faculty of Computers and Artificial Intelligence, Cairo University
The desktop client for AutoPipeline β a Next.js 16 UI wrapped in an Electron 41 shell. Point it at a local codebase, describe the pipeline you want, and watch the AI agent analyze, plan, generate, validate, and self-correct a production-ready CI/CD workflow live, streamed token-by-token. Approve commands and answer questions mid-run, then save the generated YAML to disk.
This is the frontend layer of AutoPipeline. It talks to the Backend BFF (NestJS), which proxies the Agent (Python/FastAPI). For the full experience, run all three together via the AutoPipeline launcher.
- Real-Time Streaming Chat β Reads a newline-delimited JSON (NDJSON) event stream from the backend and renders each event into an incrementally-updating timeline: assistant text, live reasoning/thinking, tool calls, plans, validation results, and YAML drafts β in the exact order they happened.
- Desktop-Native Capabilities β An Electron main process provides the three
things a browser can't: a project folder picker, encrypted API-key
storage (OS keychain via
safeStorage), and a file-save bridge for the generated workflow. - Human-in-the-Loop (HITL) β Interactive cards let you allow/deny shell commands and answer clarification questions mid-run; each decision opens a resume stream that continues the same message.
- Multi-Provider LLM Selection β Pick a built-in provider/model (OpenAI, Anthropic, Gemini, Groq) or register a custom OpenAI-compatible endpoint (Ollama, vLLM, LM Studio). API keys are supplied per-request and stored encrypted β never in this repo.
- Per-Message Controls β Choose the target CI platform (GitHub Actions / GitLab CI) and the reasoning effort on each prompt.
- Session Management β Create, switch, auto-title, and delete chat sessions, with optimistic updates and resilient history rehydration.
- Neo-Brutalist Theming β A monospace, terminal-inspired UI with class-based light/dark mode and no flash-of-wrong-theme on load.
| Layer | Technology |
|---|---|
| UI Framework | Next.js 16 (App Router, Turbopack, static export) |
| View Library | React 19 |
| Desktop Shell | Electron 41 |
| Desktop Storage | electron-store + Electron safeStorage |
| Language | TypeScript 5 (strict) |
| Styling | Tailwind CSS 4 |
| Markdown | react-markdown + remark-gfm |
| Icons | lucide-react |
| Testing | Vitest 2 (+ v8 coverage) |
| Runtime | Node.js β₯ 20 |
git clone <repo-url>
cd frontend
npm installCreate a .env file (or copy .env-example). Only one variable is read:
# Backend BFF base URL (NestJS server)
NEXT_PUBLIC_BACKEND_URL=http://localhost:3333# Full desktop experience (Next dev server + Electron window):
npm run electron-dev
# Web only (browser tab β no folder picker / keychain / save-to-disk):
npm run devThe frontend serves at http://localhost:3001.
Note: LLM API keys are not configured here. You enter them at runtime in the app's model selector; they are stored encrypted in your OS keychain and sent per-request to the backend.
The app is an Electron desktop client with a layered Next.js renderer that talks to the backend over REST + NDJSON. The renderer never touches the Python agent directly β everything flows through the BFF.
βββββββββββββββββββββ Electron Desktop App βββββββββββββββββββββ
β electron/main.js ββcontextBridgeβββΊ preload.js β
β (window, IPC, safeStorage, window.electronAPI β
β dialogs, electron-store) β β
β βΌ β
β Next.js renderer (src/) β
β app/ ββΊ components/ ββΊ hooks/ ββΊ services/ ββΊ fetch βββββββββΌβββΊ Backend BFF (:3333)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
app/β App Router routes (home / sessions / settings); thin wiring.components/β presentational cards + theSessionsLayoutorchestrator.hooks/β all stateful logic (useSessions,useMessages,useChat,useApiKeys), including the streaming reducer.services/β everyfetchcall, the NDJSON stream parser, and the Electron wrapper.types//data//utils/β shared types, the model seed list, helpers.
Application code lives under src/ (mirroring the backend and agent repos);
electron/ and public/ stay at the root. The @/ import alias resolves to
./src/*.
frontend/
βββ electron/ # Desktop shell (main process, preload bridge)
βββ public/ # Static assets (logo, title images)
βββ src/
β βββ app/ # Next.js App Router (layout, home, sessions, settings)
β βββ components/
β β βββ chat/ # AgentMessage + streaming cards (tool, plan, draft, HITLβ¦)
β β βββ sessions/ # SessionsLayout (orchestrator), sidebar, chat input, modals
β β βββ settings/ # API-key & custom-model management
β β βββ ui/ # Button, Spinner, CornerAccent
β β βββ (shell) # Navbar, ThemeProvider, ThemeToggle, ScanlineOverlay
β βββ hooks/ # useSessions, useMessages, useChat, useApiKeys
β βββ services/ # projectService, sessionService, modelService, messages, electronService
β βββ types/ # agent (StreamEvent), message, session, LLM, streamData, electron.d.ts
β βββ data/ # LLM_OPTIONS seed list
β βββ utils/ # formatRelativeTime
βββ diagrams/ # generate_diagrams.py + output/*.png
βββ docs/ # DOCUMENTATION.md, TESTING.md
βββ next.config.ts # output: 'export'
βββ tsconfig.json # paths: { "@/*": ["./src/*"] }
The frontend reads the backend base URL from NEXT_PUBLIC_BACKEND_URL and calls:
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/projects |
Initialize the selected project |
GET / DELETE |
/sessions |
List / delete chat sessions |
GET |
/messages?sessionId= |
Load & rehydrate conversation history |
POST |
/messages?projectId&modelId[&sessionId] |
Send a prompt (NDJSON stream) |
POST |
/messages/permission Β· /messages/clarification |
Resume after a HITL prompt (NDJSON) |
POST |
/messages/cancel |
Cancel an active run |
GET/POST/PATCH/DELETE |
/models |
LLM catalog + custom models |
The message send body carries the prompt, the per-request apikey, the
targetPlatform, and the reasoningEffort:
{
"message": "Create a GitHub Actions CI pipeline for this project",
"apikey": "provider-api-key",
"targetPlatform": "github_actions",
"reasoningEffort": "medium"
}| Command | Description |
|---|---|
npm run dev |
Next dev server on :3001 |
npm run build |
Static production build β ./out |
npm run start |
Serve the production build on :3001 |
npm run lint |
ESLint |
npm test / npm run test:cov |
Vitest unit suite (+ coverage) |
npm run electron |
Launch the Electron shell |
npm run electron-dev |
Run Next dev + launch Electron when ready |
npm run build-electron |
Build the frontend and package with electron-builder |
For comprehensive technical documentation β architecture deep-dives, the streaming/event-rendering pipeline, component/hook/service breakdowns, the Electron bridge, the data model, and design trade-offs β see:
π Full Technical Documentation
Rendered architecture, sequence, flow, and class diagrams (one PNG per page,
A4-friendly) live in diagrams/output/ and are
regenerated with:
python diagrams/generate_diagrams.pynpm test # run the unit suite once
npm run test:watch # watch mode
npm run test:cov # with coverage report43 hermetic unit tests (Vitest) cover the streaming, rehydration, request-shaping, and error-translation logic β the frontend's real risk. See TESTING.md for methodology and coverage.
- Launch the app; select a project folder (or a recent one).
- The frontend initializes the project via the backend.
- Pick an LLM provider/model and enter its API key (stored encrypted).
- Choose target platform + reasoning effort.
- Type a prompt; watch the run stream live.
- Approve/deny commands and answer clarifications as they appear.
- Copy or save the generated workflow YAML.
This project is licensed under the terms in the LICENSE file (MIT).