Architecture Finding
Type: tech-debt/anti-pattern
Affected area: Sources/hotshot/main.swift
HotshotApp (in Sources/hotshot/main.swift) is a single NSObject class of 783 lines and 28 methods that owns at least five distinct concerns with no separation:
- App lifecycle /
NSApplicationDelegate (applicationDidFinishLaunching)
- Status-bar menu construction and state (
setupStatusBar, rebuildMenu, menuWillOpen, updateTargetLabel)
- Clipboard image watching (
startWatchingClipboard, stopWatchingClipboard, clipboardHasImage, clipboardPNGData, writePasteboard, loadPasteboard, enrichClipboardWithSavedImage)
- Filesystem screenshot watching (
startWatchingScreenshots, stopWatchingScreenshots, handleDirectoryChange, checkForNewScreenshots)
- Terminal/AppleScript injection (
sendCtrlV, runAppleScriptForResult, commands(onTTY:), detectTargetCLI, injectPath, injectViaITerm2, injectViaGenericAppleScript, focusTerminal, runAppleScript, showNotification)
By contrast, Sources/HotshotCore/HotshotCore.swift (228 lines) holds pure, dependency-free logic (path escaping, CLI classification, control-character checks, AppleScript string escaping) and is fully covered — all 17 existing tests (Tests/HotshotCoreTests/HotshotCoreTests.swift) target only this module. None of main.swift's 28 methods have any test coverage, because they're entangled with NSWorkspace, NSPasteboard, DispatchSourceFileSystemObject, and AppleScript execution directly inside one class.
Impact
- Every new capability (new terminal target, new watch mode, new injection path) grows the same untested class.
- Bugs in clipboard/screenshot watching or AppleScript injection can only be caught by manual, on-device testing — there is no way to unit-test the state machines (debounce, dedupe-by-filename, ctrl-v vs generic AppleScript branching) in CI.
- The pattern already exists in the codebase:
HotshotCore shows the project's own convention for extracting pure logic out of AppKit glue, but that extraction stopped at string/path helpers and never reached the actual watcher/injector state machines.
Recommendation
Extract three collaborator types out of HotshotApp, following the same pure-logic-vs-glue split already used for HotshotCore:
ClipboardWatcher — owns lastClipboardChangeCount/timer and the enrich/detect logic, exposing a callback/delegate HotshotApp composes.
ScreenshotWatcher — owns the DispatchSourceFileSystemObject, debounce timer, and lastSeenScreenshots set.
TerminalInjector — owns AppleScript execution (runAppleScript, runAppleScriptForResult, injectViaITerm2, injectViaGenericAppleScript, focusTerminal, sendCtrlV) behind a small protocol, so injection strategy selection (detectTargetCLI, commands(onTTY:)) can be unit-tested with a fake AppleScript runner.
HotshotApp would then become a thin coordinator wiring these three together plus the menu/status-bar UI, matching the existing split between HotshotCore (tested, pure) and the app shell (glue).
Note: this repo has no accessible Swift toolchain/macOS runner in this environment to validate a refactor build, so this is filed as an issue only, not a PR — a maintainer with a macOS/Swift build should implement and verify it.
Filed by architect agent (ACMM L6 — full mode)
🐝 Hive Agent: architect | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown
— hive: agent=architect backend=copilot model=claude-sonnet-5 copilot=1.0.88
Architecture Finding
Type: tech-debt/anti-pattern
Affected area:
Sources/hotshot/main.swiftHotshotApp(inSources/hotshot/main.swift) is a singleNSObjectclass of 783 lines and 28 methods that owns at least five distinct concerns with no separation:NSApplicationDelegate(applicationDidFinishLaunching)setupStatusBar,rebuildMenu,menuWillOpen,updateTargetLabel)startWatchingClipboard,stopWatchingClipboard,clipboardHasImage,clipboardPNGData,writePasteboard,loadPasteboard,enrichClipboardWithSavedImage)startWatchingScreenshots,stopWatchingScreenshots,handleDirectoryChange,checkForNewScreenshots)sendCtrlV,runAppleScriptForResult,commands(onTTY:),detectTargetCLI,injectPath,injectViaITerm2,injectViaGenericAppleScript,focusTerminal,runAppleScript,showNotification)By contrast,
Sources/HotshotCore/HotshotCore.swift(228 lines) holds pure, dependency-free logic (path escaping, CLI classification, control-character checks, AppleScript string escaping) and is fully covered — all 17 existing tests (Tests/HotshotCoreTests/HotshotCoreTests.swift) target only this module. None ofmain.swift's 28 methods have any test coverage, because they're entangled withNSWorkspace,NSPasteboard,DispatchSourceFileSystemObject, and AppleScript execution directly inside one class.Impact
HotshotCoreshows the project's own convention for extracting pure logic out of AppKit glue, but that extraction stopped at string/path helpers and never reached the actual watcher/injector state machines.Recommendation
Extract three collaborator types out of
HotshotApp, following the same pure-logic-vs-glue split already used forHotshotCore:ClipboardWatcher— ownslastClipboardChangeCount/timer and the enrich/detect logic, exposing a callback/delegateHotshotAppcomposes.ScreenshotWatcher— owns theDispatchSourceFileSystemObject, debounce timer, andlastSeenScreenshotsset.TerminalInjector— owns AppleScript execution (runAppleScript,runAppleScriptForResult,injectViaITerm2,injectViaGenericAppleScript,focusTerminal,sendCtrlV) behind a small protocol, so injection strategy selection (detectTargetCLI,commands(onTTY:)) can be unit-tested with a fake AppleScript runner.HotshotAppwould then become a thin coordinator wiring these three together plus the menu/status-bar UI, matching the existing split betweenHotshotCore(tested, pure) and the app shell (glue).Note: this repo has no accessible Swift toolchain/macOS runner in this environment to validate a refactor build, so this is filed as an issue only, not a PR — a maintainer with a macOS/Swift build should implement and verify it.
Filed by architect agent (ACMM L6 — full mode)
🐝 Hive Agent:
architect| Instance:hosted-available-oke-11-placeholder-r05x| SHA:unknown— hive: agent=architect backend=copilot model=claude-sonnet-5 copilot=1.0.88