Skip to content

refactor(1/3): extract shared production helpers to reduce code duplication - #3

Merged
terryso merged 30 commits into
mainfrom
refactor/1-prod-helpers
Jun 9, 2026
Merged

refactor(1/3): extract shared production helpers to reduce code duplication#3
terryso merged 30 commits into
mainfrom
refactor/1-prod-helpers

Conversation

@terryso

@terryso terryso commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

29 iterations of shared helper extraction in production code, eliminating code duplication across Agent, ToolExecutor, MCP, LLM client, Store, and utility layers.

New shared infrastructure

File Shared helpers Purpose
LLMResponseHelpers.swift (NEW) extractFirstTextFromResponse, stripCodeFences, parseJSONToDict, parseLLMResponseAsObject/Array LLM response parsing
EnvUtils.swift (expanded) makeISO8601DateFormatter, makeSDKJSONEncoder/Decoder, validatePathSafeIdentifier, atomicWriteJSON, ensureDirectoryExists, djb2Hash, yamlEscape/yamlQuote, jsonStringify, resolveDir, defaultDir Central utility hub
LLMClient.swift (expanded) performLLMRequest, validateLLMHTTPResponse, resolveBaseURL Shared LLM client infrastructure
MCPTypes.swift (expanded) schemaToMCPValue, anyToMCPValue, mcpValueToAny, registerToolsOnMCPServer, createMCPSession, ToolExecutionError Shared MCP infrastructure
ReviewTools.swift (expanded) reviewErrorResponse, requireNonEmptyInput Review tool helpers
Agent.swift (internal) 17+ private helpers (resolveSessionMessages, trackTurnCost, buildToolContext, etc.) Agent internal dedup
ToolExecutor.swift (internal) returnToolError, emitToolStarted, executeToolWithLifecycle Tool execution dedup

Key changes

  • Agent.swift: 17+ internal helpers extracted, promptImpl + stream reduced ~200 lines combined
  • AnthropicClient + OpenAIClient unified via shared LLMClient.swift helpers
  • InProcessMCPServer + AgentMCPServer + MCPClientManager unified via shared MCPTypes.swift helpers
  • All home directory resolution centralized in EnvUtils.swift
  • All ISO8601DateFormatter construction centralized via makeISO8601DateFormatter()
  • Skill.withBaseDir() method added for clean copy-with-one-field-changed pattern

Verification

  • ✅ Zero public API changes — all new helpers are internal/private
  • ✅ Pure refactoring — no behavioral changes

🤖 Generated with Claude Code

terryso added 30 commits June 9, 2026 11:16
…ication and cyclomatic complexity: SandboxChecker.matchesAllowedPathList(), OpenAIClient.buildRequestBody(), and Agent.closedQueryResult().
…eateTraceRecorder) from duplicated code in Agent.swift's two largest methods, reducing promptImpl from 678→625 lines and stream from 1120→1092 lines, with all 6209 tests passing.
…essionMessages) that eliminated 7 duplicated inline blocks across promptImpl and stream, reducing promptImpl from 641→610 lines and stream from 1108→1078 lines with all 6212 tests passing.
…createSubAgentSpawner) that eliminated duplicated skill resolution logic across executeSkill/executeSkillStream and SubAgentSpawner construction across promptImpl/stream, with all 5682 tests passing.
… executeToolWithLifecycle) from ToolExecutor.swift's executeSingleTool method, eliminating 5× duplicated error-return patterns, 2× ToolStartedEvent emission blocks, and 2× full tool execution lifecycle blocks, reducing executeSingleTool from ~250→99 lines with all 6211 tests passing.
…rver) from MCPClientManager.swift, eliminating 3× duplicated 6-case switch dispatch blocks across connectAll/reconnect/setServers and 2× duplicated 4-line server removal blocks in setServers, reducing the file from 622→610 lines with all 5684+12 tests passing.
…sultFromExecute, executionErrorResult) from ToolBuilder.swift, eliminating duplicated input decoding pipeline across CodableTool/StructuredCodableTool (2×26 identical lines), duplicated result mapping across RawInputTool/StructuredCodableTool (2×13 identical lines), and duplicated error-return catch blocks across 3 tool structs (3×6 identical lines), reducing the file from 430→409 lines with all 5682+12 tests passing.
…ppendFallbackEntry) from Compact.swift, eliminating 3× duplicated LLM response text extraction blocks and 3× duplicated fallback SessionMemoryEntry construction blocks, reducing the file from 485→477 lines with all 5682+12 tests passing.
…oryStore.swift, eliminating 2× duplicated 26-line filter/sort/limit blocks across InMemoryStore.query and FileBasedMemoryStore.query, reducing the file from 391→377 lines with all 5682+12 tests passing.
…ponse, stripCodeFences) into new LLMResponseHelpers.swift, eliminating 3× duplicated extractTextFromResponse implementations and 3× duplicated stripCodeFences implementations across LLMSkillEvolver.swift, PromptEvolverEngine.swift, and LLMExperienceExtractor.swift, reducing those 3 files by 93 lines combined with all 5682+12 tests passing.
…hropicClient.swift (eliminating 2×11-line duplicated URLError catch blocks) and validatePathSafeIdentifier() into EnvUtils.swift (eliminating 3×10-line duplicated path traversal validation methods across SessionStore, FactStore, and MemoryStore), net -18 lines with all 5682+12 tests passing.
…SkillsDir, atomicWriteJSON) into EnvUtils.swift, eliminating 2× duplicated skills directory resolution blocks (~15 lines each) and 2× duplicated atomic JSON file write patterns (~40 lines each) from SkillUsageStore and SkillCuratorStore, net -36 lines with all 5684+12 tests passing.
…ryDir()/defaultMemoryDir shared helpers into EnvUtils.swift, eliminating 9× duplicated 5-line dateFormatter initializations across store files and Logger, plus 2× duplicated ~18-line resolveMemoryDir methods from MemoryStore and FactStore, net -71 lines with all 5682+12 tests passing.
…wift (unifying 3 duplicated JSON parsing implementations from Agent.swift, OpenAIClient.swift, and Streaming.swift) and replaced 4 inline ISO8601DateFormatter constructions in RunTracker, RunRecoveryService, and ExperienceTypes with makeISO8601DateFormatter() calls, net -26 lines with all 6212+12 tests passing.
…lidateLLMHTTPResponse, resolveBaseURL) into LLMClient.swift, unifying duplicated URLError handling, HTTP response validation, and URL resolution between AnthropicClient and OpenAIClient, net -33 lines with all 6212+12 tests passing.
…ted, defaultHomeDir) eliminating 5× duplicated stop+sessionEnd hook blocks, 2× duplicated LLMRequestStartedEvent emissions, and 2× duplicated home directory resolution blocks, net -12 lines with all 5682+12 tests passing.
…BudgetExceededInStream) eliminating 2× duplicated error classification blocks, 3× duplicated budget check patterns, and 2× duplicated ~35-line budget exceeded handling blocks, net -3 lines with all 5682+12 tests passing.
…LMResponseAsObject, parseLLMResponseAsArray) into LLMResponseHelpers.swift, eliminating the identical trim→stripCodeFences→data→JSONSerialization pipeline duplicated across LLMSkillEvolver, PromptEvolverEngine, and LLMExperienceExtractor, with all 6212+12 tests passing.
…keSDKJSONDecoder) into EnvUtils.swift, eliminating 3× duplicated djb2 hash implementations + 1 inline hash + 3× duplicated JSON encoder/decoder initialization blocks + 3× inline decoder constructions across 6 files, net -28 lines with all 5682+12 tests passing.
…ed ~12-line skill reconstructions in review tools, and replaced inline JSONEncoder/JSONDecoder in MemoryBundleExport/ImportService with shared makeSDKJSONEncoder/makeSDKJSONDecoder factories, net -28 lines with all 5682+12 tests passing.
…tInterrupted and emitAgentFailed in Agent) eliminating 7× duplicated Logger+SDKError denial blocks and 8× duplicated if-let-eventBus+AgentTerminalEvent emission patterns, net -6 lines with all 5682+12 tests passing.
…fy) into EnvUtils.swift, eliminating the last remaining #if os(Linux) home directory resolution pattern from SessionStore and unifying 2 duplicated JSON serialization functions from ConfigTool and ReadMcpResourceTool, net -25 lines with all 5682+12 tests passing.
…xt) centralizing 4× duplicated LLMCostEvent constructions and 2× duplicated 24-field ToolContext constructions across promptImpl and stream, with all 5682+12 tests passing.
…orResponse, requireNonEmptyInput) centralizing 4× duplicated createDirectory+error blocks across store files and 14× duplicated error response patterns + 9× duplicated non-empty guard patterns across review tools, net -41 lines with all 5682+12 tests passing.
…ft, centralizing 4× duplicated ~28-line cost-tracking pipelines (TokenUsage parsing → estimateCost → recordUsage → emitLLMCostEvent → recordCostBreakdown) across promptImpl fallback path, promptImpl main loop, stream messageStart, and stream messageDelta, reducing Agent.swift from 3619→3586 lines with all 5682+12 tests passing.
…efaultSubAgentSpawner.swift and refactored simple spawn to delegate to enhanced spawn, reducing the file from 197→169 lines (-28 lines) by eliminating ~45 lines of duplicated tool filtering, Agent creation, and result processing logic across 2 spawn methods, with all 5682+12 tests passing.
…Value, anyToMCPValue, mcpValueToAny) into MCPTypes.swift, eliminating 8 duplicated private methods across InProcessMCPServer, MCPClientManager, and AgentMCPServer, net -54 lines with all 5682+12 tests passing.
…ver, createMCPSession) and moved ToolExecutionError to MCPTypes.swift, eliminating duplicated tool registration loop (~45 lines), session creation (2×5 lines), and error struct (2×8 lines) across InProcessMCPServer and AgentMCPServer, net -28 lines with all 5682+12 tests passing.
… EnvUtils.swift and 2 default directory constants (defaultTracesDir, defaultApiRunsDir), eliminating duplicated YAML escaping from CuratorRunReport and SkillWriter and inline home directory resolution from TraceRecorder and RunPersistenceService, net -6 lines with all 5682+12 tests passing.
… helper infrastructure from complexity reduction refactoring

- project-context.md: added rules 48-56 (shared utility patterns, test infrastructure), updated file counts in rule 35
- architecture.md: added LLMResponseHelpers.swift and AgentEventSSEMapping.swift to Utils/ listing, added shared test infrastructure files to Tests/ listing
@terryso
terryso merged commit 5007795 into main Jun 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant