Files
ClaudeDo/src/ClaudeDo.Worker/CLAUDE.md

4.0 KiB

ClaudeDo.Worker

ASP.NET Core hosted service that executes tasks via Claude CLI in isolated environments.

Architecture

  • Program.cs — loads config, inits schema, registers DI, configures SignalR on /hub, binds to 127.0.0.1:47821
  • QueueServiceBackgroundService with two execution slots:
    • Queue slot: FIFO sequential processing of "agent"-tagged queued tasks
    • Override slot: immediate execution via RunNow(taskId)
    • Wake signaling via SemaphoreSlim, backstop timer (30s default)
  • StaleTaskRecovery — startup-only service, flips orphaned "running" tasks to "failed"

Task Execution Pipeline

TaskRunner orchestrates:

  1. Load task + list metadata from DB; resolve config from list_config + task-level overrides (model, system_prompt, agent_path)
  2. Create worktree (if WorkingDir set) or sandbox directory
  3. Mark task "running", broadcast TaskStarted
  4. Build CLI args via ClaudeArgsBuilder; invoke ClaudeProcess with task prompt
  5. Stream NDJSON output through StreamAnalyzer; lines forwarded to log file and SignalR (TaskMessage)
  6. On success: auto-commit changes (worktree only), store run record, mark "done"
  7. On failure: retry once if session ID available (--resume), then mark "failed"

Key Components

  • ClaudeProcess — spawns claude -p --output-format stream-json --verbose --dangerously-skip-permissions. Writes prompt to stdin, reads NDJSON from stdout. Supports CancellationToken (kills process tree).
  • ClaudeArgsBuilder — dynamically constructs CLI args; supports --model, --append-system-prompt, --agents, --json-schema, --resume
  • StreamAnalyzer — parses rich NDJSON output; extracts session_id, token counts, turn counts, result text, structured output. Replaces MessageParser.
  • TaskResetService — discards a failed task's worktree and resets the task row to Manual; preserves run history.
  • WorktreeManager — creates worktrees at claudedo/{taskId[:8]} branches, commits changes with semantic messages, updates DB with head commit and diff stats
  • CommitMessageBuilder — formats {commitType}(slug): title\n\ndescription\n\nClaudeDo-Task: taskId
  • AgentFileService — manages ~/.todo-app/agents/*.md agent definition files; exposes list/refresh via SignalR
  • LogWriter — async StreamWriter wrapper, auto-creates parent dirs

Execution History

Each CLI invocation is recorded in the task_runs table via TaskRunRepository:

  • Fields: session_id, input/output/cache token counts, turn count, result text, structured output JSON
  • Enables auto-retry on failure (resume last session) and multi-turn follow-up via ContinueAsync

Multi-Turn / Continue

TaskRunner.ContinueAsync sends a follow-up prompt to an existing Claude session using --resume <session_id> with the stored session ID from the last run.

SignalR Hub

WorkerHub methods: Ping, GetActive, RunNow, CancelTask, WakeQueue, ContinueTask, ResetTask, GetAgents, RefreshAgents, GetAppSettings, UpdateAppSettings, CleanupFinishedWorktrees, ResetAllWorktrees, MergeTask, GetMergeTargets, UpdateList, UpdateListConfig, GetListConfig, UpdateTaskAgentSettings

HubBroadcaster events: TaskStarted, TaskFinished, TaskMessage, WorktreeUpdated, TaskUpdated, RunCreated, ListUpdated

Config

Loaded from ~/.todo-app/worker.config.json:

  • db_path, sandbox_root, log_root
  • worktree_root_strategy ("sibling" | "central"), central_worktree_root
  • queue_backstop_interval_ms (default 30000)
  • signalr_port (default 47821)
  • claude_bin (path to claude CLI)

Per-list config (list_config in DB) provides defaults for model, system_prompt, agent_path; tasks can override each individually.

Notes

  • The worker runs standalone — start it separately from the UI
  • Only listens on loopback (127.0.0.1)
  • ClaudeProcess uses --dangerously-skip-permissions — tasks run with full filesystem access
  • Worktree branches follow claudedo/{id} naming convention