- Create types/ directory with centralized domain types: session, tool-instance, tool-type, git-repository, config-folder, tool-config, project, user, api-response - Remove inline type definitions from API modules; re-export from types/ for backward compatibility - Update state/sessions.tsx to import Session from types/session.ts - Update all consumer components/pages to import from types/ - Extract seed_builtin_tool_types from main.py to seeds/builtin_tool_types.py - Create types/index.ts barrel export Quality gates: tsc (pass), eslint (pass), Python syntax (pass)
23 KiB
Repo Restructure — Exploration Report
Project: Headquarter (full-stack workspace platform) Date: 2026-06-02 Scope: Comprehensive codebase audit for structural refactoring
1. Directory Structure
Root Layout
/workspace
├── apps/
│ ├── web/ # React 18 + Vite frontend
│ └── api/ # Python FastAPI + SQLAlchemy backend
├── e2e/ # Playwright tests
├── docs/ # (not heavily populated)
└── openspec/ # OpenSpec changes
Frontend (apps/web/src/)
src/
├── api/ # 13 API modules (~1,200 LOC total)
│ ├── client.ts
│ ├── dashboard.ts
│ ├── git_repositories.ts
│ ├── profile.ts
│ ├── projects.ts
│ ├── sessions.ts
│ ├── settings.ts
│ ├── ssh_keys.ts
│ ├── terminal.ts
│ ├── tool_configs.ts
│ ├── tool_types.ts
│ ├── config_folders.ts
│ └── config_profiles.ts
├── components/ # 16 components (~2,100 LOC total)
│ ├── app-shell.tsx
│ ├── code-editor.tsx
│ ├── commit-dialog.tsx
│ ├── commit-panel.tsx
│ ├── file-editor.tsx
│ ├── git-toolbar.tsx
│ ├── icon.tsx
│ ├── instance-list.tsx
│ ├── merge-dialog.tsx
│ ├── protected-route.tsx
│ ├── protected-route.test.tsx
│ ├── repository-create-dialog.tsx
│ ├── settings-tab-layout.tsx
│ ├── syntax-highlighter.tsx
│ ├── workspace-header.tsx
│ └── repositories-settings-tab.tsx
├── hooks/ # 1 hook
│ └── use-theme.ts
├── pages/ # 15 pages (~3,500 LOC total)
│ ├── dashboard.tsx
│ ├── git-history.tsx
│ ├── git-repositories.tsx
│ ├── placeholder.tsx
│ ├── profile.tsx
│ ├── project-settings.tsx
│ ├── projects.tsx
│ ├── repo-workspace.tsx
│ ├── settings.tsx
│ ├── ssh-keys.tsx
│ ├── terminal.tsx
│ ├── tool-configs.tsx
│ ├── tool-types.tsx
│ └── tool-workshop.tsx
├── state/ # 2 context providers
│ ├── auth.tsx
│ └── sessions.tsx
├── types/ # 2 type modules
│ └── terminal.ts
├── utils/ # 3 utilities
│ ├── icons.ts
│ ├── language.ts
│ └── terminal-protocol.ts
├── styles.css # 1 massive stylesheet (2,844 lines)
├── router.tsx # Route definitions
├── main.tsx # Entry point
└── types.ts # Shared domain types
Backend (apps/api/)
apps/api/
├── src/
│ ├── main.py # App entry point (~287 lines)
│ ├── config.py # Pydantic settings (~128 lines)
│ ├── database.py # SQLAlchemy setup (~114 lines)
│ ├── logging_config.py # Middleware & logging (~92 lines)
│ ├── auth/
│ │ ├── session.py
│ │ └── dependencies.py
│ ├── api/ # 15 routers
│ │ ├── auth.py
│ │ ├── config_folders.py
│ │ ├── config_profiles.py
│ │ ├── dashboard.py
│ │ ├── git_repositories.py # ~900+ lines
│ │ ├── health.py
│ │ ├── instance_proxy.py
│ │ ├── projects.py
│ │ ├── ssh_keys.py
│ │ ├── terminal.py
│ │ ├── tool_configs.py
│ │ ├── tool_instances.py # ~1,463 lines — CRITICAL
│ │ ├── tool_types.py
│ │ ├── user_config.py
│ │ └── users.py
│ ├── models/ # SQLAlchemy models
│ ├── services/ # Business logic
│ │ ├── docker.py # ~457+ lines
│ │ ├── docker_build.py
│ │ ├── git_control.py
│ │ ├── git_files.py
│ │ ├── git_history.py
│ │ ├── git_url_parser.py
│ │ ├── profile_resolver.py
│ │ └── readiness_probe.py
│ ├── utils/ # Additional utilities
│ └── scripts/
│ └── seed.py
├── alembic/versions/ # 14+ migrations
└── tests/
├── conftest.py
├── unit/
└── integration/
2. File Sizes — Files Over 200 Lines
🔴 CRITICAL — Over 400 Lines (Must Split)
| File | Lines | Issue |
|---|---|---|
apps/web/src/styles.css |
2,844 | Single stylesheet for entire app; mixes layout, components, pages, syntax highlighting, and themes |
apps/api/src/api/tool_instances.py |
1,463 | Monolithic router: CRUD, Docker orchestration, tunneling, proxying, config resolution, readiness probes |
apps/api/src/api/git_repositories.py |
~900+ | Combined file browsing, Git control (branch/commit/merge/push/pull), URL parsing, history |
apps/api/src/services/docker.py |
~457+ | Docker compose, container management, tunneling, config folder staging all in one |
🟡 WARNING — Over 200 Lines (Should Split)
| File | Lines | Issue |
|---|---|---|
apps/web/src/pages/tool-workshop.tsx |
~700+ | 3-tab admin page with inline forms for tool types, configs, AND folders |
apps/web/src/pages/sessions.tsx |
~668 | Sessions page with create form, active/recent lists, inline confirmations |
apps/web/src/pages/repo-workspace.tsx |
~394 | Page + FileBrowser component + mixed data loading |
apps/web/src/components/instance-list.tsx |
~388 | Instance CRUD + health checks + create dialog |
apps/web/src/pages/tool-types.tsx |
~380 | Tool types list + create/edit dialog inline |
apps/web/src/pages/tool-configs.tsx |
~354 | Tool configs list + create/edit dialog inline |
apps/web/src/hooks/use-terminal-connection.ts |
~439 | WS lifecycle, ping-pong, reconnection, local echo, resize debouncing |
apps/web/src/pages/dashboard.tsx |
~338 | Summary cards, session lists, quick-create form, recent sessions |
apps/web/src/components/terminal.tsx |
~309 | Terminal chrome + xterm lifecycle + resize observer |
apps/web/src/pages/git-history.tsx |
~233 | Commit list + detail panel with inline formatting |
apps/web/src/api/git_repositories.ts |
~245 | API functions + types (reasonable, but types should move) |
apps/web/src/pages/projects.tsx |
~206 | List + create dialog + delete confirmation |
apps/api/src/main.py |
~287 | Router registration + startup logic + seeding + error handlers |
apps/api/src/api/config_profiles.py |
~877 | Config profiles CRUD + complex resolution logic |
apps/api/src/api/tool_types.py |
~616 | Tool types CRUD + compose/dockerfile validation |
apps/api/src/api/config_folders.py |
~372 | Config folders CRUD |
3. Frontend Module Analysis
Components (16 files, ~2,100 LOC, avg ~131 LOC)
Too large:
git-toolbar.tsx(~268) — mixes git ops, branch creation form, merge dialog trigger, status summaryfile-editor.tsx(~241) — view/edit/commit workflowinstance-list.tsx(~388) — instance CRUD + health + create dialogterminal.tsx(~309) — terminal chrome + xterm lifecycle
Well-sized:
workspace-header.tsx(~48)protected-route.tsx(~19)icon.tsx(~165)
Pages (15 files, ~3,500 LOC, avg ~233 LOC)
All pages are too large. Every page mixes:
- Data fetching (useEffect + API calls)
- Local state management (useState for forms, dialogs, loading)
- UI rendering (JSX)
Worst offenders:
tool-workshop.tsx(~700) — 3 completely different admin interfaces in one filesessions.tsx(~668) — create form + active/recent lists + confirmationsrepo-workspace.tsx(~394) — containsFileBrowsercomponent inlinedashboard.tsx(~338) — summary, active sessions, projects list, quick-create form
Hooks (3 files)
use-theme.ts(~23) — fineuse-terminal-connection.ts(~439) — too large; mixes WS lifecycle, ping-pong, reconnection, echo, resize
API Modules (13 files, ~1,200 LOC)
- Well-organized by domain
- Inconsistency: Some define types inline (
api/sessions.tsdefinesToolInstance,Session), others in separatetypes.ts api/client.ts— centralized Axios instance with auth interceptor. Good pattern.
State/Context (2 files)
auth.tsx(~63) — well-sizedsessions.tsx(~44) — well-sized
Styles (1 file, 2,844 lines) — CRITICAL
styles.css is the biggest problem in the frontend. It contains:
- CSS variables / design tokens
- Global resets
- Layout (shell, nav, content grid)
- Page styles (home, settings, git-history, repo-workspace)
- Component styles (cards, buttons, dialogs, forms, file-tree, editor)
- Syntax highlighting overrides
- Responsive media queries scattered throughout
Types
src/types.ts— core domain types (SessionUser, Project)src/types/terminal.ts— terminal-specific WebSocket protocol types- Problem: API modules also export their own types (
ToolInstance,Session,GitRepository, etc.) causing duplication and confusion.Sessionis defined in BOTHapi/sessions.tsandstate/sessions.tsx.
Utils
icons.ts(~180) — icon name mappinglanguage.ts(~90) — file extension → language detectionterminal-protocol.ts(~76) — WS message encoding/decoding + type guards
Router
router.tsx(~58) — clean and readable
Tests
components/protected-route.test.tsx(~49)api/tool_types.test.ts(~227)api/config_folders.test.ts(~131)pages/dashboard.test.tsx(~81)pages/projects.test.tsx(~174) — failing tests (React Router context issue)pages/tool-workshop.test.tsx(~527)hooks/use-terminal-connection.test.ts(~339)- Massive gaps: No tests for most pages, hooks, state providers, or components
4. Backend Module Analysis
Entry Points
src/main.py(~287) — FastAPI app setup, CORS, middleware, exception handlers, startup events, seeding, router mounting- Problem: Seed data (builtin tool types) is hardcoded here (~100 lines of compose templates). Should be in
seeds/orservices/seed_data.py.
Routers/Endpoints (15 files)
Organization: One router per domain — good structure in theory, but files are too large.
tool_instances.py (1,463 lines) — The worst offender. Contains:
- Pydantic request/response models
- Helper functions:
_modify_compose_file,_apply_resolved_profile,_get_user,_get_owned_project,_sanitize_name,_generate_instance_name - Endpoints: create, list, get, start, stop, restart, delete, logs, recreate-tunnel, health-check, proxy
- Inline Docker orchestration logic (should be in services)
- Inline config resolution (should use service layer)
git_repositories.py (~900+ lines) — Contains:
- Repository CRUD
- File browsing endpoints
- Git control endpoints (branch, checkout, commit, fetch, pull, push, merge)
- URL parsing endpoint
config_profiles.py (~877 lines) — Contains:
- Config profile CRUD
- Complex profile resolution logic
- Config folder/application logic
Models
- Located in
src/models/— one file per entity - Clean separation, well-sized
Services/Business Logic
docker.py(~456) — Docker compose, container ops, tunneling, config file staging. Too large.docker_build.py(~69) — Image buildinggit_control.py(~295) — Git operationsgit_files.py(~439) — File tree, read, writegit_history.py(~382) — Commit history, graph, diffgit_url_parser.py(~228) — URL parsing and validationprofile_resolver.py(~251) — Config profile resolutionreadiness_probe.py(~66) — Container health probesterminal_manager.py(~193) — Terminal session lifecycleterminal_session.py(~162) — Individual terminal session handling
Database/ORM
database.py(~116) — Engine, session factory, init with alembic subprocessconfig.py(~143) — Pydantic settings with env var resolution- Alembic migrations in
alembic/versions/— 14+ migration files
5. Coupling and Dependency Patterns
Frontend High-Coupling Files
repo-workspace.tsx imports from:
react-router-dom(params, search params)../api/client(direct apiClient usage)../api/git_repositories../components/commit-panel../components/file-editor../components/git-toolbar../components/instance-list../components/workspace-header../api/tool_types
dashboard.tsx imports from:
../api/dashboard,../api/sessions,../api/projects,../api/git_repositories,../api/tool_types,../api/settings../types,../components/icon
tool-workshop.tsx imports from:
../api/tool_types,../api/tool_configs,../api/config_folders- Manages 3 separate entity forms with ~20 useState variables each
Circular Dependencies
- No obvious circular imports detected, but
Sessiontype is duplicated betweenapi/sessions.tsandstate/sessions.tsx, creating conceptual circularity.
Business Logic Mixed with UI
- Every page component contains API calls directly in
useEffect - Form validation logic is inline in page components
repo-workspace.tsxdefinesFileBrowseras an inner component — cannot be tested or reused independently
API Call Patterns
- Mostly centralized in
api/modules — good - Exception:
repo-workspace.tsx,file-editor.tsx,project-settings.tsxuseapiClientdirectly instead of domain API modules - Exception:
app-shell.tsxcallsgetUserSessions()directly
6. Naming Inconsistencies
File Naming Conventions
| Location | Convention | Examples | Issues |
|---|---|---|---|
pages/ |
mostly kebab-case | git-history.tsx, repo-workspace.tsx |
projects.tsx, profile.tsx, settings.tsx, dashboard.tsx are NOT kebab-case |
components/ |
kebab-case | app-shell.tsx, protected-route.tsx |
repositories-settings-tab.tsx (long but consistent) |
api/ |
snake_case | tool_configs.ts, git_repositories.ts |
Mixes with frontend convention |
utils/ |
kebab-case | terminal-protocol.ts |
Good |
hooks/ |
camelCase | useTheme.ts would be standard, but file is use-theme.ts |
Actually kebab-case, which is fine but inconsistent with React convention |
| Backend routers | snake_case | tool_instances.py, git_repositories.py |
Consistent within backend |
| Backend services | snake_case | docker.py, profile_resolver.py |
Consistent |
Component vs File Naming
- Component
ProtectedRoute→ fileprotected-route.tsx✅ - Component
AppShell→ fileapp-shell.tsx✅ - Component
GitHistoryPage→ filegit-history.tsx❌ (should beGitHistoryPageingit-history-page.tsxOR component renamed toGitHistory) - Component
RepoWorkspace→ filerepo-workspace.tsx❌ (same issue) - Page components use
Pagesuffix inconsistently:ProjectsPage,GitHistoryPage, butRepoWorkspacehas noPagesuffix
Function/Variable Naming
- Frontend: camelCase consistently
- Backend: snake_case consistently
- API types: Backend uses
snake_casefields; frontend types mirror this (default_ssh_key_id,tool_type_name). Good for API alignment.
7. Quality Signals
TODO/FIXME Comments
- Only 2 TODOs found:
apps/api/src/utils/git_history.py:188-189:# TODO: extract committer separately(appears twice)
This is surprisingly low — suggests either good maintenance or lack of inline documentation.
Dead Code / Unused Exports
dashboard.tsxexportsHomePage as DashboardPage— dual naming is confusingsrc/types.tsexportsSessionPayloadwhich is only used in auth context- Several CSS classes in
styles.cssmay be unused (hard to verify without build analysis)
Duplicate Logic
- Backend auth checks:
_get_user()and_get_owned_project()are duplicated in nearly every router file (tool_instances.py,git_repositories.py,ssh_keys.py, etc.) - Frontend loading/error patterns: Identical
status: "loading" | "ready" | "error"state + retry button pattern copied in ~8 page components - Frontend form dialogs: Create/edit/delete confirmation pattern repeated in
projects.tsx,tool-types.tsx,tool-configs.tsx,ssh-keys.tsx
Test Coverage Gaps
- Frontend: 7 test files, but many pages and components untested
- Backend: Unit tests for
git_url_parser.py,migration_metadata.py,profile_resolver.py,readiness_probe.py,docker_build.py,terminal_manager.py,terminal_session.py; integration tests viaconftest.py - E2E tests only cover login flow (
e2e/tests/login.spec.ts)
Recommendations
Target Directory Structure
Frontend (apps/web/src/)
src/
├── api/ # Keep — centralized API layer
│ ├── client.ts
│ ├── __mocks__/ # Add: mock API responses for tests
│ └── {domain}/ # Group by domain
│ ├── index.ts # Re-exports
│ ├── types.ts # Domain types ONLY
│ └── api.ts # API functions
├── components/ # Generic UI components
│ ├── ui/ # Primitive components (Button, Card, Dialog, Input)
│ ├── layout/ # AppShell, Navigation, Header
│ └── features/ # Domain-specific components
│ ├── git/
│ ├── project/
│ ├── session/
│ └── settings/
├── hooks/ # Custom hooks
│ ├── use-theme.ts
│ ├── use-auth.ts # Extract from state/auth.tsx?
│ └── use-api-query.ts # NEW: reusable data fetching
├── pages/ # Route entry points ONLY
│ ├── dashboard/
│ │ └── page.tsx
│ ├── projects/
│ │ ├── page.tsx
│ │ ├── project-list.tsx
│ │ └── create-project-dialog.tsx
│ └── ...
├── state/ # Keep contexts
├── styles/
│ ├── tokens.css # CSS variables only
│ ├── global.css # Resets + base styles
│ ├── components/ # Component styles
│ └── pages/ # Page-specific styles
├── types/ # Centralize ALL shared types
│ └── index.ts
└── utils/
Backend (apps/api/src/)
src/
├── main.py # Router mounting + middleware ONLY
├── config.py
├── database.py
├── logging_config.py
├── auth/
├── api/
│ └── v1/ # Versioned routes
│ ├── __init__.py
│ ├── auth.py
│ ├── projects/
│ │ ├── __init__.py
│ │ ├── router.py
│ │ └── dependencies.py
│ ├── repositories/
│ │ ├── __init__.py
│ │ ├── router.py # CRUD only
│ │ ├── files.py # File browsing
│ │ └── git.py # Git control operations
│ ├── instances/
│ │ ├── __init__.py
│ │ ├── router.py # CRUD + lifecycle
│ │ ├── compose.py # Compose file generation
│ │ ├── tunnel.py # Cloudflare tunnel ops
│ │ └── proxy.py # HTTP proxy
│ └── ...
├── models/
├── schemas/ # NEW: Pydantic schemas separate from routers
├── services/
│ ├── docker/
│ │ ├── __init__.py
│ │ ├── compose.py # Extract from docker.py
│ │ ├── container.py # Container lifecycle
│ │ ├── tunnel.py # Cloudflare tunneling
│ │ └── config.py # Config file staging
│ └── git/
│ ├── control.py
│ ├── files.py
│ └── history.py
├── seeds/ # NEW: Seed data
│ └── builtin_tool_types.py
└── tests/
Files That MUST Be Split
apps/web/src/styles.css→ Split into 5-8 files by concernapps/api/src/api/tool_instances.py→ Split into router + compose service + tunnel service + proxy serviceapps/api/src/api/git_repositories.py→ Split into repository CRUD router + file router + git control routerapps/api/src/services/docker.py→ Split into compose, container, tunnel, config staging modulesapps/web/src/pages/tool-workshop.tsx→ Split into 3 page tabs or feature componentsapps/web/src/pages/repo-workspace.tsx→ ExtractFileBrowsertocomponents/features/git/file-browser.tsxapps/web/src/pages/sessions.tsx→ Extract create form, active list, recent listapps/web/src/hooks/use-terminal-connection.ts→ Extract WS manager, echo handler, resize debouncer
Naming Convention to Standardize On
| Layer | Convention | Example |
|---|---|---|
| React components (files) | PascalCase matching component | GitHistoryPage.tsx |
| React hooks (files) | camelCase | useTheme.ts |
| Utility modules | kebab-case | terminal-protocol.ts |
| API modules | kebab-case | tool-configs.ts |
| Backend routers | snake_case | tool_instances.py |
| Backend services | snake_case | profile_resolver.py |
| CSS modules | kebab-case matching component | git-history-page.module.css |
Order of Migration (First → Last)
Phase 1: Safe Foundations (low risk)
- Extract shared types to
src/types/index.ts(remove duplication) - Create
src/hooks/use-api-query.tsfor reusable data fetching - Extract
FileBrowserfromrepo-workspace.tsx - Move seed data from
main.pytoseeds/builtin_tool_types.py
Phase 2: Style System (medium risk, high reward)
5. Split styles.css into design tokens + component modules
6. Introduce CSS modules or Tailwind utility extraction for component styles
Phase 3: Backend Decomposition (medium risk)
7. Extract _get_user and _get_owned_project to auth/dependencies.py or api/dependencies.py
8. Split tool_instances.py into router + services
9. Split git_repositories.py into CRUD + files + git control routers
10. Split services/docker.py into focused modules
Phase 4: Frontend Page Decomposition (higher risk — touches UX)
11. Split tool-workshop.tsx into feature components
12. Split dashboard.tsx into summary/session/project sections
13. Split sessions.tsx into create-form + lists
14. Split settings.tsx — move GeneralSettingsTab to its own file
Phase 5: Testing & Polish 15. Add tests for extracted components 16. Add backend integration tests for refactored routers