Skip to content

Refactor: decompose god files, restructure into domains/, add typed abstractions#38

Merged
DorianZheng merged 10 commits intomainfrom
worktree-agile-questing-robin
Mar 22, 2026
Merged

Refactor: decompose god files, restructure into domains/, add typed abstractions#38
DorianZheng merged 10 commits intomainfrom
worktree-agile-questing-robin

Conversation

@DorianZheng
Copy link
Copy Markdown
Collaborator

Summary

Major codebase refactoring for maintainability and readability, guided by research across 20+ open-source TypeScript projects (VS Code, Outline, Cal.com, Twenty CRM, Ghost, etc.).

Phase 1: Decompose 8 god files into ~70 focused modules

  • agent-manager.ts (3,387 lines) → 14 modules under agents/
  • client-api.ts (1,253 lines) → 16 domain handler files under client-handlers/
  • sandbox-manager.ts (1,345 lines) → 12 modules under sandboxes/
  • agent-chat-view.ts (3,351 lines) → 5 Lit components
  • sandboxes-view.ts (2,520 lines) → 7 Lit components
  • app-shell.ts (1,963 → 1,282 lines), agent-profile-panel.ts (1,656 → 493 lines), settings-view.ts (1,286 → 150 lines)
  • slack-connection.ts (849 lines) → 5 modules, api/agents.ts (1,184 → 550 lines)

Phase 2: Restructure file hierarchy

  • Group 6 domain dirs under domains/ (agents, sandboxes, slack, host, mailbox, todos)
  • Merge host-operator/ + host-commands/ into domains/host/
  • Delete 10 dead REST API files (replaced by WS RPC gateway)
  • Eliminate 6 barrel re-export files (anti-pattern per community consensus)
  • Remove redundant file prefixes (e.g., agent-chat-view.tschat-view.ts in agents/)
  • Dissolve frontend components/layout/ kitchen sink into messages/, settings/, modals/, sidebar/

Phase 3: Final cleanup

  • Merge mailbox/ and todos/ into agents/ (tightly coupled)
  • Move misplaced utils (host-directory-picker.ts, mentions.ts) to their domains
  • Move agent-prompts/ and agent-skills/ into domains/agents/

Phase 4: Code abstraction improvements

  • Typed event bus (gateway/events.ts) — discriminated union for WS broadcasts
  • Shared async lock utility (utils/async-lock.ts)
  • Storage helpers (storage/helpers.ts) — JSON parsing, bool conversion
  • Agent runtime grouping — RunningAgent fields organized into sub-objects
  • Status transition validation — warns on invalid state transitions
  • Message delivery types — foundation for future messaging unification

Design principles applied

  • Unix philosophy: each function does one thing
  • No redundant file prefixes (directory provides namespace)
  • No barrel files in application code (only at package boundaries)
  • Plain functions over classes (idiomatic TypeScript)
  • Grouped domains pattern (proven by Ghost 60 domains, Twenty 20, Amplication 51)

Test plan

  • make build passes for all packages (backend, frontend, electron)
  • make test — no new test failures (pre-existing failures unchanged)
  • No stale import paths remaining (verified via grep)
  • Manual: make dev — app starts, agents can be created/started/messaged
  • Manual: Slack integration still works
  • Manual: Sandbox creation and exec still work

Split the monolithic agent-manager.ts by shared mutable state analysis:
- runtime-state.ts: owns runningAgents Map, agent locks, runtime singleton
- lifecycle.ts: startAgent/stopAgent orchestration
- messaging.ts: sendMessage + Claude CLI command building
- todo-reminder.ts: idle reminders, leader PDCA, policy detection
- daemon-sync.ts: communication daemon lifecycle
- settings-sync.ts: Claude settings merge + push
- runtime-sandbox.ts: runtime sandbox CRUD
- container-exec.ts: stateless BoxLite exec wrappers
- prompt-builder.ts: system prompt assembly
- host-paths.ts: host filesystem setup
- skills-sync.ts: skill directory sync
- nginx.ts: miniapp nginx routing
- screen.ts: screenshot + screen info
- constants.ts: all shared constants and types

agent-manager.ts becomes a 61-line barrel re-export for backward
compatibility with all existing consumers (17 test files, 7 source files).
Split the monolithic RPC handler map by domain prefix:
- client-handlers/channels.ts, messages.ts, agents.ts, mounts.ts
- client-handlers/memory.ts, mailbox.ts, apps.ts, host-ops.ts
- client-handlers/todos.ts, settings.ts, admin.ts, sandboxes.ts
- client-handlers/slack.ts, media.ts, validation.ts, index.ts

client-api.ts becomes a 3-line barrel re-export.
Each handler file uses registerXxxHandlers(h) pattern for clean composition.
Split by domain: lifecycle, exec, files, ACL, resource conversion,
path helpers, runtime state, terminal, and shared exec helpers.
sandbox-manager.ts becomes an 8-line barrel re-export.
Extract three sub-components from the monolithic chat view:
- memory-editor.ts: <agent-memory-editor> with file list, editor, resize
- host-settings.ts: <agent-host-settings-panel> with operator settings
- stash-strip.ts: <agent-stash-strip> with offline message queue
- chat-view.css.ts: extracted CSS styles

Parent communicates via properties down, CustomEvents up.
agent-chat-view.ts slimmed from 3351 to 852 lines.
Extract tab components from the monolithic sandbox view:
- overview-tab.ts, execs-tab.ts, files-tab.ts, terminal-tab.ts
- create-dialog.ts for new sandbox creation
- view.css.ts for shared styles
sandboxes-view.ts slimmed to ~260 lines (card grid + tab switching).
app-shell.ts (1963 -> 1282 lines):
- Extract app-shell.css.ts, host-approval-modal.ts, workspace-sync.ts

agent-profile-panel.ts (1656 -> 493 lines):
- Extract profile-tab.ts, skills-tab.ts, logs-tab.ts, system-prompt-overlay.ts

settings-view.ts (1286 -> 150 lines):
- Extract settings-model-section.ts, settings-slack-section.ts
slack-connection.ts (849 -> 34 lines barrel):
- connection.ts: lifecycle, start/stop
- event-router.ts: message handling, send APIs
- approval-notify.ts: approval request posting
- agent-sync.ts: agent <-> Slack sync
- channel-sync.ts: channel <-> Slack sync

api/agents.ts (1184 -> 550 lines):
- agents-validation.ts: shared validation helpers
- agents-memory.ts: memory file CRUD routes
- agents-mounts.ts: mount CRUD routes
Backend:
- Group 6 domain dirs under domains/ (agents, sandboxes, slack, host,
  mailbox, todos) — keeps top-level scannable (8 items vs 16)
- Merge host-operator/ + host-commands/ into domains/host/
  (gui-service.ts, gui-provider.ts, shell-service.ts)
- Delete 10 dead api/ REST route files (replaced by WS RPC gateway)
- Delete 6 barrel re-export files (agent-manager.ts, index.ts,
  sandbox-manager.ts, slack-connection.ts, client-api.ts)
- Update all 40+ consumer files to import specific sub-modules

Frontend:
- Remove redundant agent-/channel-/sandboxes- prefixes from 12 files
  (directory already provides namespace)
- Keep @CustomElement tag names unchanged

Research-backed decisions (20 projects analyzed):
- Grouped domains pattern: Ghost (60), Twenty (20), Amplication (51)
- No barrel files: VS Code, Cal.com, TkDodo consensus
- No redundant prefixes: universal convention
Backend:
- Merge mailbox/ and todos/ into domains/agents/ (agent sub-concerns)
- Move agent-prompts/ and agent-skills/ into domains/agents/
- Move utils/host-directory-picker.ts to domains/host/
- Move utils/mentions.ts to domains/agents/
- Move api/admin.ts to domains/host/admin.ts
- Delete empty api/, websocket/, mailbox/, todos/ directories

Frontend:
- Dissolve components/layout/ kitchen sink into proper directories:
  - message-area + codex-composer → components/messages/
  - settings-view + sections → components/settings/
  - host-approval-modal → components/modals/
  - sidebar-panel → components/sidebar/

Backend top-level: 8 items (was 16). Clean domain/infra separation.
Frontend components: 8 directories, each with clear purpose.
1. Typed event bus (gateway/events.ts) — discriminated union for all
   WS broadcast events, compile-time payload safety
2. Async lock utility (utils/async-lock.ts) — shared keyed lock
   pattern used by both agents and sandboxes
3. Storage helpers (storage/helpers.ts) — shared JSON parsing,
   bool conversion used across store files
4. Agent runtime grouping — RunningAgent fields grouped into
   session/execution/interrupt/daemon/ports sub-objects
5. Status transition validation — warns on invalid agent state
   transitions (e.g., stopped → responding)
6. Message delivery types (domains/agents/message-types.ts) —
   shared types as foundation for future messaging unification

All changes are structural (better types, deduplication, grouping).
No behavior changes.
@DorianZheng DorianZheng merged commit 3980f71 into main Mar 22, 2026
1 check failed
@DorianZheng DorianZheng deleted the worktree-agile-questing-robin branch March 22, 2026 01:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant