refactor: split the CLI into per-domain extension files#78
Merged
Conversation
CT3-lite (nuclear-review, final item): CLI/programa.swift had grown to 14,664 lines. Split CMUXCLI's methods along its existing domain seams into 8 new extension files, verbatim, along with the pbxproj wiring for the programa-cli target. No behavior change. Per-file line counts (post-split, includes new file boilerplate): CLI/programa.swift 6259 struct decl, args, run(), the #76 command-descriptor table, shared arg-parsing/handle-formatting/ socket-client helpers, usage and version/welcome text CLI/CLI+Markdown.swift 103 markdown commands CLI/CLI+SSH.swift 1329 ssh / remote relay CLI/CLI+Browser.swift 1310 browser command family CLI/CLI+Themes.swift 727 theme selection/config CLI/CLI+Tree.swift 544 tree command CLI/CLI+TmuxCompat.swift 1927 __tmux-compat translation layer CLI/CLI+AgentWrappers.swift 893 claude-teams/omo/omx/omc CLI/CLI+Hooks.swift 1660 Claude/Codex hook session logic ------------------------------------ total 14752 (original 14664 + 88 lines of import/extension boilerplate across the 8 new files) Stored properties stay in the main file (Swift forbids stored instance properties in extensions); only `let args: [String]` exists. Widened from `private` to `internal` only where the compiler forced it (cross-file references), 58 symbols total: Shared helpers in CLI/programa.swift now called from the new domain files: resolvePath, sanitizedFilenameComponent, bestEffortPruneTemporaryFiles, looksLikePath, authenticateClientIfNeeded, formatIDs, intFromAny, doubleFromAny, parseBoolString, isHandleRef, normalizeWindowHandle, normalizeWorkspaceHandle, normalizePaneHandle, normalizeSurfaceHandle, formatHandle, printV2Payload, resolveWorkspaceId, resolveSurfaceId, parseOption, optionValue, hasFlag, workspaceFromArgsOrEnv, textHandle, v2OKSummary, isUUID, jsonString, resolvedVersionInfo, parentSearchURL, candidateInfoPlistURLs, resolvedExecutableURL. Command entry points referenced by name in commandDescriptors()'s dispatch closures (which stay in the main file): runMarkdownCommand, runSSH, runSSHSessionEnd, runRemoteDaemonStatus, runBrowserCommand, runThemes, runTreeCommand, tmuxCompatDescriptors (static), runClaudeTeamsTmuxCompat, runTmuxCompatCommand, runClaudeTeams, runOMO, runOMX, runOMC, runClaudeHook, runCodexInstallHooks, runCodexUninstallHooks, runCodexHook, tmuxPruneCompatWorkspaceState, tmuxPruneCompatSurfaceState, resolveWorkspaceIdAllowingFallback, resolveSurfaceIdAllowingFallback. Cross-domain-file helpers: struct TmuxCompatFocusedContext and tmuxCompatFocusedContext, configureTmuxCompatEnvironment, createTmuxCompatShimDirectory (all in CLI+TmuxCompat.swift, called from CLI+AgentWrappers.swift); writeShimIfChanged (CLI+AgentWrappers.swift, called from CLI+TmuxCompat.swift); mergedNodeOptions (CLI+Hooks.swift, called from CLI+AgentWrappers.swift's configureAgentWrapperEnvironment). project.pbxproj: added the 8 new files to the programa-cli target's Sources build phase and CLI group (fresh B90000xx IDs, no collisions). Verified: programa-cli, programa, and programa-unit (build-for-testing) all build green under PROGRAMA_SKIP_ZIG_BUILD=1; the built CLI binary's --version and help output diffed byte-identical against a binary built from pre-split main.
This was referenced Jul 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
CLI/programa.swift had grown to 14,664 lines — everything the
programaCLI does lived in one file. This splits it into 8 domain files (markdown, ssh, browser, themes, tree, tmux-compat, agent wrappers, hooks) plus a slimmed-down main file, with zero behavior change. This is the last item (CT3-lite) from the recent nuclear-review maintainability audit.Summary
Moved CMUXCLI's methods verbatim into new extension files along their existing domain seams:
CLI/programa.swiftargs,run(), the #76 command-descriptor table, shared arg-parsing/handle-formatting/socket-client helpers, usage/version/welcome textCLI/CLI+Markdown.swiftCLI/CLI+SSH.swiftCLI/CLI+Browser.swiftCLI/CLI+Themes.swiftCLI/CLI+Tree.swiftCLI/CLI+TmuxCompat.swift__tmux-compattranslation layerCLI/CLI+AgentWrappers.swiftrunAgentWrapper)CLI/CLI+Hooks.swiftTotal 14752 lines = original 14664 + 88 lines of
import/extension CMUXCLI { }boilerplate across the 8 new files.Stored properties stayed in the main file (Swift forbids stored instance properties in extensions) — there's only one,
let args: [String].The only functional-looking change is access-level widening, done strictly where the compiler forced it (cross-file references), 58 symbols total:
programa.swiftnow called from the new domain files:resolvePath,sanitizedFilenameComponent,bestEffortPruneTemporaryFiles,looksLikePath,authenticateClientIfNeeded,formatIDs,intFromAny,doubleFromAny,parseBoolString,isHandleRef,normalizeWindowHandle,normalizeWorkspaceHandle,normalizePaneHandle,normalizeSurfaceHandle,formatHandle,printV2Payload,resolveWorkspaceId,resolveSurfaceId,parseOption,optionValue,hasFlag,workspaceFromArgsOrEnv,textHandle,v2OKSummary,isUUID,jsonString,resolvedVersionInfo,parentSearchURL,candidateInfoPlistURLs,resolvedExecutableURL.commandDescriptors()'s dispatch closures (which stay in the main file):runMarkdownCommand,runSSH,runSSHSessionEnd,runRemoteDaemonStatus,runBrowserCommand,runThemes,runTreeCommand,tmuxCompatDescriptors(static),runClaudeTeamsTmuxCompat,runTmuxCompatCommand,runClaudeTeams,runOMO,runOMX,runOMC,runClaudeHook,runCodexInstallHooks,runCodexUninstallHooks,runCodexHook,tmuxPruneCompatWorkspaceState,tmuxPruneCompatSurfaceState,resolveWorkspaceIdAllowingFallback,resolveSurfaceIdAllowingFallback.struct TmuxCompatFocusedContext+tmuxCompatFocusedContext,configureTmuxCompatEnvironment,createTmuxCompatShimDirectory(all inCLI+TmuxCompat.swift, called fromCLI+AgentWrappers.swift);writeShimIfChanged(CLI+AgentWrappers.swift, called fromCLI+TmuxCompat.swift);mergedNodeOptions(CLI+Hooks.swift, called fromCLI+AgentWrappers.swift'sconfigureAgentWrapperEnvironment).GhosttyTabs.xcodeproj/project.pbxproj: added all 8 new files to theprograma-clitarget's Sources build phase and CLI group (freshB90000xxIDs, verified no collisions).Test Plan
xcodebuild -scheme programa-cli(Debug, macOS) — BUILD SUCCEEDEDxcodebuild -scheme programa(Debug, macOS) — BUILD SUCCEEDEDxcodebuild -scheme programa-unit build-for-testing(Debug, macOS) — TEST BUILD SUCCEEDED--versionandhelpoutput diffed byte-identical against a binary built from pre-split main (no socket running for either)