Skip to content

Split headless mode into separate daemon command - #33

Merged
balloob merged 11 commits into
mainfrom
claude/split-headless-daemon-mH8Tw
Jan 3, 2026
Merged

balloob merged 11 commits into
mainfrom
claude/split-headless-daemon-mH8Tw

Conversation

@balloob

@balloob balloob commented Jan 3, 2026 •

Copy link
Copy Markdown
Contributor
  • Create new daemon.py module for headless operation
  • Add 'daemon' subcommand to CLI (sendspin daemon)
  • Deprecate --headless flag with warning, route to daemon mode
  • Remove headless branching from app.py (TUI mode only)
  • Update CLAUDE.md with new architecture diagram and file responsibilities
  • Daemon and TUI modes now cleanly separated for independent evolution

Breaking change: Use 'sendspin daemon' instead of --headless. For now only prints a warning.

claude and others added 6 commits January 3, 2026 15:50
- Create new daemon.py module for headless operation
- Add 'daemon' subcommand to CLI (sendspin daemon)
- Deprecate --headless flag with warning, route to daemon mode
- Remove headless branching from app.py (TUI mode only)
- Update CLAUDE.md with new architecture diagram and file responsibilities
- Daemon and TUI modes now cleanly separated for independent evolution

Breaking change: Recommended to use 'sendspin daemon' instead of --headless
Flatten exception handling structure by removing unnecessary nesting:
- Move signal handler setup before main try block
- Combine cleanup operations in single finally block
- Reduce indentation and improve code readability

No functional changes, just cleaner exception handling flow.
Flatten exception handling from two try-finally blocks to one:
- Move discovery.stop() into the main finally block
- Remove outer try-finally that only stopped discovery
- All cleanup now happens in single finally block

Benefits:
- Simpler structure: 2 levels → 1 level of nesting
- Easier to understand cleanup flow
- Same cleanup guarantees, less code
Directory structure:
- sendspin/tui/        - TUI mode (app.py, ui.py, keyboard.py)
- sendspin/daemon/     - Daemon mode (daemon.py)
- sendspin/           - Shared modules (audio, discovery, etc.)

Changes:
- Create tui/ and daemon/ directories with __init__.py
- Move mode-specific files to respective packages
- Update all imports to use new package paths
- Update CLAUDE.md with new architecture diagram

Benefits:
- Clearer separation of concerns
- Prepares for independent evolution of TUI and daemon modes
- Better organization as codebase grows
Daemon mode now:
- Only has PLAYER role (no CONTROLLER, no METADATA)
- Uses logging instead of print_event
- No state tracking (AppState, metadata, group updates)
- No TUI imports - completely independent
- Simplified connection logic - just connect and play audio
- 165 lines vs previous 216 lines

Shared utilities:
- Move get_device_info() to utils.py for reuse
- Both tui and daemon import from shared utils

Benefits:
- Daemon is truly minimal - just plays audio
- Clear separation: TUI tracks state, daemon just plays
- Easier to maintain and understand
- No unnecessary complexity in headless mode
@balloob
balloob force-pushed the claude/split-headless-daemon-mH8Tw branch from 436d9bd to 7d8690a Compare January 3, 2026 15:16
@balloob
balloob merged commit c3c354c into main Jan 3, 2026
1 check passed
@balloob
balloob deleted the claude/split-headless-daemon-mH8Tw branch January 3, 2026 15:44
@balloob balloob added refactor Changes code without changing behavior breaking-change Changes CLI options, settings, hooks or the protocol in a way that is not backwards compatible labels Jan 4, 2026
trisweb pushed a commit to trisweb/sendspin-cli that referenced this pull request Jan 5, 2026
* Split headless mode into separate daemon command

- Create new daemon.py module for headless operation
- Add 'daemon' subcommand to CLI (sendspin daemon)
- Deprecate --headless flag with warning, route to daemon mode
- Remove headless branching from app.py (TUI mode only)
- Update CLAUDE.md with new architecture diagram and file responsibilities
- Daemon and TUI modes now cleanly separated for independent evolution

Breaking change: Recommended to use 'sendspin daemon' instead of --headless

* Simplify nested try-finally blocks in app.py and daemon.py

Flatten exception handling structure by removing unnecessary nesting:
- Move signal handler setup before main try block
- Combine cleanup operations in single finally block
- Reduce indentation and improve code readability

No functional changes, just cleaner exception handling flow.

* Combine try-finally blocks into single level

Flatten exception handling from two try-finally blocks to one:
- Move discovery.stop() into the main finally block
- Remove outer try-finally that only stopped discovery
- All cleanup now happens in single finally block

Benefits:
- Simpler structure: 2 levels → 1 level of nesting
- Easier to understand cleanup flow
- Same cleanup guarantees, less code

* Organize code into tui/ and daemon/ package directories

Directory structure:
- sendspin/tui/        - TUI mode (app.py, ui.py, keyboard.py)
- sendspin/daemon/     - Daemon mode (daemon.py)
- sendspin/           - Shared modules (audio, discovery, etc.)

Changes:
- Create tui/ and daemon/ directories with __init__.py
- Move mode-specific files to respective packages
- Update all imports to use new package paths
- Update CLAUDE.md with new architecture diagram

Benefits:
- Clearer separation of concerns
- Prepares for independent evolution of TUI and daemon modes
- Better organization as codebase grows

* Simplify daemon to be completely independent from TUI

Daemon mode now:
- Only has PLAYER role (no CONTROLLER, no METADATA)
- Uses logging instead of print_event
- No state tracking (AppState, metadata, group updates)
- No TUI imports - completely independent
- Simplified connection logic - just connect and play audio
- 165 lines vs previous 216 lines

Shared utilities:
- Move get_device_info() to utils.py for reuse
- Both tui and daemon import from shared utils

Benefits:
- Daemon is truly minimal - just plays audio
- Clear separation: TUI tracks state, daemon just plays
- Easier to maintain and understand
- No unnecessary complexity in headless mode

* More cleanup

* Remove extra comment

* Remove extra comment

* Add reconnect for daemon back

* only use discovery when no url given

* Standardize audio resolving

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Changes CLI options, settings, hooks or the protocol in a way that is not backwards compatible refactor Changes code without changing behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants