Skip to content

refactor: split the CLI into per-domain extension files#78

Merged
arzafran merged 1 commit into
mainfrom
refactor/ct3-split-cli
Jul 8, 2026
Merged

refactor: split the CLI into per-domain extension files#78
arzafran merged 1 commit into
mainfrom
refactor/ct3-split-cli

Conversation

@arzafran

@arzafran arzafran commented Jul 8, 2026

Copy link
Copy Markdown
Member

What this does

CLI/programa.swift had grown to 14,664 lines — everything the programa CLI 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:

File Lines Contents
CLI/programa.swift 6259 struct decl, args, run(), the #76 command-descriptor table, shared arg-parsing/handle-formatting/socket-client helpers, usage/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 (share runAgentWrapper)
CLI/CLI+Hooks.swift 1660 Claude/Codex hook session logic

Total 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:

  • Shared helpers in 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 + 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).

GhosttyTabs.xcodeproj/project.pbxproj: added all 8 new files to the programa-cli target's Sources build phase and CLI group (fresh B90000xx IDs, verified no collisions).

Test Plan

  • xcodebuild -scheme programa-cli (Debug, macOS) — BUILD SUCCEEDED
  • xcodebuild -scheme programa (Debug, macOS) — BUILD SUCCEEDED
  • xcodebuild -scheme programa-unit build-for-testing (Debug, macOS) — TEST BUILD SUCCEEDED
  • Smoke test: built CLI binary's --version and help output diffed byte-identical against a binary built from pre-split main (no socket running for either)

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.
@arzafran arzafran merged commit a3e64d2 into main Jul 8, 2026
8 checks passed
@arzafran arzafran deleted the refactor/ct3-split-cli branch July 10, 2026 16:05
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