Skip to content

Refactor daemon.ts — extract modules and break down monster functions #174

@xliry

Description

@xliry

Problem

daemon.ts is 1528 lines with 2 monster functions: runClaude() (324 lines) and main() (222 lines). Settings merge logic is duplicated, git operations are scattered, and there are 7 global state variables. This makes the code hard to test, debug, and extend.

What to do

Step 1: Extract src/logging.ts

Move these functions out of daemon.ts:

  • rotateLogs() (lines 190-199)
  • checkRotate() (lines 200-216)
  • out() / err() (lines 217-230)
  • logMemory() (lines 231-260)
  • formatEvent() (lines 804-874)
  • Export: initLogging(agentName), out(), err(), logMemory(), formatEvent(), checkRotate()

Step 2: Extract src/process.ts

Move Claude subprocess management:

  • runClaude() (lines 969-1292) — break into sub-functions:
    • cleanEnvironment() — env cleanup (977-995)
    • setupClaudePermissions(globalSettingsPath, workspaceSettingsPath) — settings merge logic (1004-1040 + 1096-1118, currently DUPLICATED)
    • spawnClaude(args, cwd, env) — process creation (1120-1124)
    • handleClaudeTimeout(child, config, taskId) — timeout guard (1129-1163)
    • handleClaudeCompletion(code, work, config, worktreeInfo, branchName) — post-exit merge/cleanup (1195-1243)
  • The main runClaude() should be a thin orchestrator calling these sub-functions (~50 lines max)
  • Export: runClaude(config, work, phase, prompt)

Step 3: Extract src/recovery.ts

Move task recovery logic:

  • recoverStaleTasks() (lines 329-422)
  • checkRuntimeStaleTasks() (lines 423-494)
  • These two share duplicated patterns (fetch tasks, filter by agent, post comment, update status) — extract common performRecovery(tasks, reason, maxRetries) helper
  • Export: recoverStaleTasks(config), checkRuntimeStaleTasks(config)

Step 4: Extract src/comments.ts

Move comment baseline tracking:

  • refreshCommentBaselines() (lines 596-606)
  • loadCommentBaselines() (lines 607-626)
  • saveCommentBaselines() (lines 627-660)
  • Export: loadCommentBaselines(), saveCommentBaselines(), refreshCommentBaselines()

Step 5: Extract src/prompt.ts

Move prompt building:

  • sanitizeTaskBody() (lines 661-676)
  • resolveBuildCmd() (lines 677-687)
  • buildPrompt() (lines 688-803) — split into phase-specific functions:
    • buildCommentPrompt(work, config)
    • buildPlanPrompt(work, config)
    • buildExecutePrompt(work, config)
    • buildSinglePrompt(work, config)
  • Export: buildPrompt(phase, work, config)

Step 6: Simplify main() in daemon.ts

After extractions, main() should be:

  1. Parse args
  2. Init logging
  3. Write PID
  4. Recover stale tasks
  5. Poll loop: checkForWork → runClaude → sleep
  6. Shutdown cleanup

Target: main() should be ~80-100 lines, not 222.

Step 7: Remove duplicate settings merge

The settings merge logic appears twice (lines 1004-1040 and 1096-1118). Extract into a single mergeClaudeSettings(filePath, allowedTools, deniedTools) function in process.ts.

Rules

  • Keep ALL existing functionality — this is a pure refactoring, no behavior changes
  • Each new module should have proper TypeScript exports
  • daemon.ts should import from the new modules
  • Global state variables (logStream, activeAgentName, lastSeenComments, currentProcess, busy, stopped, sleepResolve) should stay in daemon.ts or be encapsulated where they naturally belong
  • PID management functions can stay in daemon.ts (they're small and focused)

Files

  • src/daemon.ts — shrink from 1528 to ~400-500 lines
  • src/logging.ts — NEW (~100 lines)
  • src/process.ts — NEW (~300 lines)
  • src/recovery.ts — NEW (~120 lines)
  • src/comments.ts — NEW (~70 lines)
  • src/prompt.ts — NEW (~150 lines)

Acceptance

  • npm run build passes with zero errors
  • daemon.ts is under 500 lines
  • No function over 100 lines
  • No duplicate code
  • All existing tests still pass (if any)
  • Agent still completes tasks successfully after refactor

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions