CodexMonitor is a macOS Tauri app that orchestrates Codex agents across local workspaces. The frontend is React + Vite; the backend is a Tauri Rust process that spawns codex app-server per workspace and streams JSON-RPC events.
src/App.tsx: composition rootsrc/features/: feature-sliced UI, hooks, and local helperssrc/features/settings/components/SettingsView.tsx: settings UI (projects, display, Codex)src/features/update/components/UpdateToast.tsx: in-app updater UIsrc/features/home/components/Home.tsx: home dashboard + latest agent runssrc/features/settings/hooks/useAppSettings.ts: app settings load/save + doctorsrc/features/update/hooks/useUpdater.ts: update checks + install flowsrc/features/layout/hooks/useResizablePanels.ts: panel resize + persistencesrc/features/composer/hooks/useComposerImages.ts: image attachment statesrc/features/composer/hooks/useComposerImageDrop.ts: drag/drop + paste imagessrc/features/git/hooks/useGitHubIssues.ts: GitHub issues tab datasrc/utils/threadItems.ts: thread item normalization + conversionsrc/services/tauri.ts: Tauri IPC wrappersrc/styles/: split CSS by areasrc/types.ts: shared typessrc-tauri/src/lib.rs: backend app-server clientsrc-tauri/src/git.rs: git status/log/diff + GitHub issues viaghsrc-tauri/src/settings.rs: app settings persistencesrc-tauri/src/codex_config.rs: read/write Codexconfig.tomlfeature flagssrc-tauri/src/prompts.rs: custom prompt discovery/parsingsrc-tauri/tauri.conf.json: window config + effects
- Composition root: keep orchestration in
src/App.tsx; avoid logic in components. - Components: presentational only; props in, UI out; no Tauri IPC calls.
- Hooks: own state, side-effects, and event wiring (e.g., app-server events).
- Utils: pure helpers live in
src/utils/(no React hooks here). - Services: all Tauri IPC goes through
src/services/(prefersrc/services/tauri.ts; event subscriptions can live insrc/services/events.ts). - Types: shared UI data types live in
src/types.ts. - Styles: one CSS file per UI area in
src/styles/(no global refactors in components). - Backend IPC: add new commands in
src-tauri/src/lib.rsand mirror them in the service. - App-server protocol: do not send any requests before
initialize/initialized. - Keep
src/App.tsxlean:- Keep it to wiring: hook composition, top-level layout, and route/section assembly.
- Move stateful logic/effects into hooks under
src/features/app/hooks/. - Keep Tauri IPC, menu listeners, and subscriptions out of
src/App.tsx(use hooks/services). - If a block grows beyond ~60 lines or needs its own state/effects, extract it.
- Backend spawns
codex app-serverusing thecodexbinary. - Initializes with
initializerequest andinitializednotification. - Streams JSON-RPC notifications over stdout; request/response pairs use
id. - Approval requests arrive as server-initiated JSON-RPC requests.
- Threads are fetched via
thread/list, filtered bycwd, and resumed viathread/resumewhen selected. - Archiving uses
thread/archiveand removes the thread from the UI list.
The app uses a shared event hub for Tauri events so each event has exactly one native listen and fan-outs to React subscribers.
- Backend emits:
src-tauri/src/lib.rsusesemit_menu_event(orapp.emit) to send events to the"main"window. - Frontend hub:
src/services/events.tsdefinescreateEventHuband module-level hubs (one per event). These hubs calllistenonce and dispatch to subscribers. Each listener call is wrapped intry/catchso one handler cannot block others. - React subscription: components/hooks call
useTauriEventwith asubscribeXfunction fromsrc/services/events.ts. Avoid callinglistendirectly from React.
- Backend emit: add a new menu item or command in
src-tauri/src/lib.rsthat callsapp.emit("event-name", payload)oremit_menu_event(...). - Frontend hub: add a hub and subscription in
src/services/events.ts:- Define the payload type (or reuse an existing one).
- Create
const myEventHub = createEventHub<MyPayload>("event-name"); - Export
subscribeMyEvent(onEvent, options)that delegates to the hub.
- React usage: wire it up with
useTauriEvent(subscribeMyEvent, handler)in a hook/component (usuallysrc/App.tsxor a feature hook). - Tests: update
src/services/events.test.tsif you add new subscription helpers.
- Workspaces are stored in
workspaces.jsonunder the app data directory. list_workspacesreturns saved items;add_workspacepersists and spawns a session.- On launch, the app connects each workspace once and loads its thread list.
src/App.tsxguards this with aSetto avoid connect/list loops.
npm install
npm run tauri devnpm run tauri buildnpm run typechecknpm run testnpm run test:watch- At the end of a task, run
npm run lintfirst. - Run
npm run testwhen you touched thread handling, settings, updater, or any shared utils. - Finish with
npm run typecheck.
- UI layout or styling: update
src/features/*/components/*andsrc/styles/*. - App-server event handling: edit
src/features/app/hooks/useAppServerEvents.ts. - Tauri IPC: add wrappers in
src/services/tauri.tsand implement insrc-tauri/src/lib.rs. - App settings or updater behavior:
src/features/settings/hooks/useAppSettings.ts,src/features/update/hooks/useUpdater.ts, andsrc/features/settings/components/SettingsView.tsx. - Experimental feature toggles: UI state in
src/features/settings/components/SettingsView.tsx, shared types insrc/types.ts, and sync to Codexconfig.tomlviasrc-tauri/src/codex_config.rs+src-tauri/src/settings.rs(daemon mirror insrc-tauri/src/bin/codex_monitor_daemon.rs). - Git diff behavior:
src/features/git/hooks/useGitStatus.ts(polling + activity refresh) andsrc-tauri/src/lib.rs(libgit2 status). - GitHub issues panel:
src/features/git/hooks/useGitHubIssues.ts+src-tauri/src/git.rs. - Thread history rendering:
src/features/threads/hooks/useThreads.tsmergesthread/resumeturns into UI items.- Thread names can come from the resume preview (when no custom name) or from the first user/assistant message when the name is auto-generated.
- Thread item parsing/normalization:
src/utils/threadItems.ts. - Thread state reducer:
src/features/threads/hooks/useThreadsReducer.ts.
The useThreads hook is a composition layer that wires together focused hooks and shared utilities. This keeps side effects isolated and makes the flow easier to test.
- Orchestration:
src/features/threads/hooks/useThreads.ts- Composes the hooks below and provides the public API to the UI.
- Actions (RPC + list/paging):
src/features/threads/hooks/useThreadActions.tsthread/start,thread/resume,thread/list, pagination, archive.- Updates activity timestamps and thread names/previews.
- Approvals (allowlist + decisions):
src/features/threads/hooks/useThreadApprovals.ts- Tracks remembered commands and resolves approval requests.
- Event handlers (server → reducer):
src/features/threads/hooks/useThreadEventHandlers.ts- Composes:
useThreadApprovalEvents.ts(approval requests + allowlist auto-accept)useThreadItemEvents.ts(item-level updates, deltas, tool/reasoning/agent items)useThreadTurnEvents.ts(turn start/complete/interrupt, plan/token/rate limit updates)
- Composes:
- Messaging:
src/features/threads/hooks/useThreadMessaging.ts- Sends user messages and handles local echo/queueing.
- Thread state/storage:
src/features/threads/hooks/useThreadStorage.ts- LocalStorage-backed custom names, pinned threads, activity map.
- Status updates (shared):
src/features/threads/hooks/useThreadStatus.ts- Centralized helpers for processing/reviewing/active turn updates.
- Selectors:
src/features/threads/hooks/useThreadSelectors.ts- Active thread ID/items for the active workspace.
- Rate limits:
src/features/threads/hooks/useThreadRateLimits.ts- Fetches and normalizes account rate limits.
- Collab links:
src/features/threads/hooks/useThreadLinking.ts- Applies parent/child links to thread state.
- Utilities:
src/features/threads/utils/threadNormalize.ts(shape normalization)src/features/threads/utils/threadStorage.ts(persistence helpers)src/utils/threadItems.ts(thread item conversion + merge)
- The window uses
titleBarStyle: "Overlay"and macOS private APIs for transparency. - Avoid breaking the JSON-RPC format; app-server rejects requests before initialization.
- The debug panel is UI-only; it logs client/server/app-server events from
useAppServerEvents. - App settings live in
settings.jsonunder the app data directory (Codex path, default access mode, UI scale). - Experimental toggles that map to Codex features (
collab,steer,unified_exec) are synced toCODEX_HOME/config.toml(or~/.codex/config.toml) on load/save and are best-effort (settings still persist if the file is missing/unwritable). - UI preferences (panel sizes, reduced transparency toggle, recent thread activity) live in
localStorage. - GitHub issues require
ghto be installed and authenticated. - Custom prompts are loaded from
$CODEX_HOME/prompts(or~/.codex/prompts) and support optional frontmatter metadata.