Files
flynn/.planning/phases/01-daemon-decomposition/01-01-PLAN.md
T

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
src/daemon/models.ts
src/daemon/memory.ts
src/daemon/tools.ts
src/daemon/index.ts
true
truths artifacts key_links
createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, and createModelRouter are importable from src/daemon/models.ts with identical signatures
Memory store, vector store, hybrid search, and background indexer initialization works identically when imported from src/daemon/memory.ts
Tool registration, web search, process tools, browser tools, tool executor, and tool policy setup works identically when imported from src/daemon/tools.ts
All 1077+ existing tests pass with zero regressions
clientFactory.test.ts continues to pass without modification
path provides exports min_lines
src/daemon/models.ts Model client factory, GitHub model mapping, auto-fallback, model router creation
createClientFromConfig
anthropicToGitHubModel
createAutoFallbackClient
createModelRouter
200
path provides exports min_lines
src/daemon/memory.ts Memory store, vector store, hybrid search, and background indexer initialization
initMemory
60
path provides exports min_lines
src/daemon/tools.ts Tool registration, web search, process tools, browser tools, tool executor, tool policy
initTools
80
from to via pattern
src/daemon/index.ts src/daemon/models.ts import and call createModelRouter import.*from.*['"]./models.js['"]
from to via pattern
src/daemon/index.ts src/daemon/memory.ts import and call initMemory import.*from.*['"]./memory.js['"]
from to via pattern
src/daemon/index.ts src/daemon/tools.ts import and call initTools import.*from.*['"]./tools.js['"]
from to via pattern
src/daemon/clientFactory.test.ts src/daemon/index.ts re-exports from models.ts import.*from.*['"]./index.js['"]
Extract model client logic, memory initialization, and tool registration from daemon/index.ts into dedicated modules.

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`:
  1. createClientFromConfig (lines 75-146) — the provider switch-case factory
  2. anthropicToGitHubModel (lines 155-184) — Anthropic-to-GitHub model name mapping
  3. createAutoFallbackClient (lines 191-207) — auto fallback for Anthropic tiers
  4. createModelRouter (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.ts exists with all four exported functions
  • createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter are importable from both ./models.js and ./index.js
  • clientFactory.test.ts passes without changes
  • No type errors
Task 2: Extract memory initialization into src/daemon/memory.ts src/daemon/memory.ts, src/daemon/index.ts Create `src/daemon/memory.ts` with a single factory function that encapsulates memory/vector/hybrid-search setup.

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.ts exists with initMemory exported
  • Memory initialization logic removed from startDaemon() body
  • All tests pass
  • No type errors
Task 3: Extract tool registration into src/daemon/tools.ts src/daemon/tools.ts, src/daemon/index.ts Create `src/daemon/tools.ts` with a factory function that encapsulates tool registry setup, web search, process tools, browser tools, tool executor, and tool policy.

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.ts exists with initTools exported
  • 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)
Before declaring plan complete: - [ ] `pnpm typecheck` passes with zero errors - [ ] `pnpm test:run` passes all 1077+ tests - [ ] `pnpm test:run src/daemon/clientFactory.test.ts` passes (re-export verification) - [ ] `src/daemon/models.ts` exports: createClientFromConfig, anthropicToGitHubModel, createAutoFallbackClient, createModelRouter - [ ] `src/daemon/memory.ts` exports: initMemory - [ ] `src/daemon/tools.ts` exports: initTools - [ ] daemon/index.ts re-exports model functions for backward compatibility - [ ] daemon/index.ts is ~650-700 lines (reduced by ~400 lines)

<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>
After completion, create `.planning/phases/01-daemon-decomposition/01-01-SUMMARY.md`