11 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-daemon-decomposition | 01 | execute | 1 |
|
true |
|
Purpose: These three extractions are the largest code movements (models ~240 lines, memory ~90 lines, tools ~70 lines) and are independent of channel/routing logic. Doing them first reduces daemon/index.ts by ~400 lines.
Output: Three new files (models.ts, memory.ts, tools.ts) with re-exports from daemon/index.ts to preserve the existing public interface.
<execution_context> @/home/will/.config/opencode/get-shit-done/workflows/execute-plan.md @/home/will/.config/opencode/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md@src/daemon/index.ts @src/daemon/clientFactory.test.ts @src/daemon/lifecycle.ts
Task 1: Extract model client logic into src/daemon/models.ts src/daemon/models.ts, src/daemon/index.ts Create `src/daemon/models.ts` containing these functions moved verbatim from `daemon/index.ts`:createClientFromConfig(lines 75-146) — the provider switch-case factoryanthropicToGitHubModel(lines 155-184) — Anthropic-to-GitHub model name mappingcreateAutoFallbackClient(lines 191-207) — auto fallback for Anthropic tierscreateModelRouter(lines 209-316) — full model router setup with tiers, fallbacks, retry
Move the necessary imports from daemon/index.ts to models.ts:
import type { Config, ModelConfig } from '../config/index.js';import { AnthropicClient, OpenAIClient, OllamaClient, LlamaCppClient, GeminiClient, BedrockClient, GitHubModelsClient, ModelRouter, DEFAULT_RETRY_CONFIG } from '../models/index.js';import type { ModelClient, RetryConfig, ModelTier } from '../models/index.js';
All four functions must be exported with the exact same signatures. JSDoc comments must be preserved.
In daemon/index.ts:
- Remove lines 70-316 (the four functions and their imports that are now only used in models.ts)
- Add
import { createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter } from './models.js'; - Add re-exports at the bottom:
export { createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter } from './models.js';
The re-exports are CRITICAL — clientFactory.test.ts imports from ./index.js and must continue to work without modification.
Keep imports in daemon/index.ts that are still needed by other code in that file (e.g., ModelRouter type for DaemonContext).
Run pnpm test:run src/daemon/clientFactory.test.ts — all tests pass.
Run pnpm typecheck — no type errors.
src/daemon/models.tsexists with all four exported functionscreateClientFromConfig,anthropicToGitHubModel,createAutoFallbackClient,createModelRouterare importable from both./models.jsand./index.js- clientFactory.test.ts passes without changes
- No type errors
Extract from startDaemon() lines 549-653 (memory store init, vector store, hybrid search, background indexer, memory tools registration) into a function:
export interface MemoryDeps {
config: Config;
dataDir: string;
lifecycle: Lifecycle;
toolRegistry: ToolRegistry;
}
export interface MemoryResult {
memoryStore?: MemoryStore;
hybridSearch?: HybridSearch;
}
export async function initMemory(deps: MemoryDeps): Promise<MemoryResult>
The function body should contain the exact logic from lines 549-653:
- Create memoryDir, mkdirSync
- Conditionally create MemoryStore
- If embedding enabled: create embedding provider, VectorStore, HybridSearch, background indexer interval, lifecycle shutdown handlers
- Register memory tools via createMemoryTools
- Return
{ memoryStore, hybridSearch }
Move only the imports that are exclusively used by memory logic:
import { MemoryStore } from '../memory/index.js';import { VectorStore, HybridSearch, createEmbeddingProvider, chunkText, contentHash } from '../memory/index.js';import type { EmbeddingProvider as EmbeddingProviderInterface } from '../memory/index.js';import { createMemoryTools } from '../tools/builtin/index.js';
In daemon/index.ts:
- Replace lines 549-653 with:
const { memoryStore, hybridSearch } = await initMemory({ config, dataDir, lifecycle, toolRegistry }); - Remove the imports that moved to memory.ts (MemoryStore stays if still referenced in DaemonContext or createMessageRouter types)
- Add
import { initMemory } from './memory.js';
Note: MemoryStore type is used in createMessageRouter deps interface. Keep the type import in daemon/index.ts if needed, or import it from memory index.
Run pnpm test:run — all tests pass (memory is integration-tested via daemon tests).
Run pnpm typecheck — no type errors.
src/daemon/memory.tsexists withinitMemoryexported- Memory initialization logic removed from
startDaemon()body - All tests pass
- No type errors
Extract from startDaemon() lines 585-714 (tool registry init, web search, process manager, browser manager, tool executor, tool policy) into:
export interface ToolsDeps {
config: Config;
lifecycle: Lifecycle;
hookEngine: HookEngine;
}
export interface ToolsResult {
toolRegistry: ToolRegistry;
toolExecutor: ToolExecutor;
browserManager?: BrowserManager;
}
export function initTools(deps: ToolsDeps): ToolsResult
Important: The function is NOT async because none of the tool setup is async (process manager, browser manager, tool policy are all synchronous). Only the shutdown handlers are async but those are callbacks registered on lifecycle.
The function body contains the exact logic from lines 585-714:
- Create ToolRegistry, register allBuiltinTools
- Register web search tools (if configured)
- Create ProcessManager, register process tools, lifecycle shutdown
- Create BrowserManager (if enabled), register browser tools, lifecycle shutdown
- Create ToolExecutor with hookEngine
- Create ToolPolicy from config, set on registry, log profile
Move the imports used exclusively by tool setup:
import { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, BrowserManager, createBrowserTools } from '../tools/index.js';
Keep in daemon/index.ts any tool imports still needed (e.g., ToolRegistry type for DaemonContext, createSessionTools, createAgentsListTool, createMessageSendTool, createCronTools, createMediaSendTool for later in startDaemon).
In daemon/index.ts:
- Replace lines 585-714 with:
const { toolRegistry, toolExecutor, browserManager } = initTools({ config, lifecycle, hookEngine }); - Remove the moved imports
- Add
import { initTools } from './tools.js';
Note: Do NOT move the "Register Tier 1 agent tools" section (lines 975-993) — those depend on sessionManager, agentConfigRegistry, channelRegistry, and cronScheduler which are created later in startDaemon. They stay in daemon/index.ts for now.
Run pnpm test:run — all tests pass.
Run pnpm typecheck — no type errors.
Count lines in daemon/index.ts — should be ~400 lines shorter than baseline (1088).
src/daemon/tools.tsexists withinitToolsexported- Tool registration logic removed from
startDaemon()body - Tier 1 agent tools (session, agents list, message send, cron) remain in daemon/index.ts
- All tests pass
- No type errors
- daemon/index.ts is ~650-700 lines (down from 1088)
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- Each new module file can be read and understood in isolation
- daemon/index.ts is measurably shorter with model/memory/tool logic extracted </success_criteria>