Skip to content

refactor(airc-bash): extract cmd_daemon family — Phase 0 monolith split#214

Merged
joelteply merged 1 commit intocanaryfrom
refactor/extract-cmd-daemon
Apr 28, 2026
Merged

refactor(airc-bash): extract cmd_daemon family — Phase 0 monolith split#214
joelteply merged 1 commit intocanaryfrom
refactor/extract-cmd-daemon

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

Splits the cmd_daemon command-group (5 cmd_X functions + 8 private helpers, 432 lines) out of the airc bash monolith into `lib/airc_bash/cmd_daemon.sh`, sourced via the same lib-dir resolver as cmd_connect.sh / cmd_doctor.sh / platform_adapters.sh.

```
airc: 5265 → 4834 lines (-431)
lib/airc_bash/cmd_daemon.sh: +461 (432 body + 29 header)
```

Stacks alongside #213 (cmd_connect, -1354 from airc). Independent — they touch different line ranges in airc and can land in either order.

Scope

Functions extracted:

  • `cmd_daemon` — verb router (install|status|uninstall|log)
  • `cmd_daemon_install` — top-level installer, branches per platform
  • `cmd_daemon_uninstall` — top-level uninstaller
  • `cmd_daemon_status` — dump platform-native unit/plist state + log tail
  • `cmd_daemon_log` — `tail` the daemon stdout log

Plus 8 `daemon*` private helpers (airc_path, scope, installed, install_done, install_launchd, install_schtasks, install_systemd).

Verification

  • `bash -n` clean on both files
  • `airc daemon status` — full plist/launchctl/systemd/schtasks readout matches pristine canary

Test plan

  • CI: clean-install smoke matrix
  • CI: integration suite on push

🤖 Generated with Claude Code

Pulls the cmd_daemon command group (cmd_daemon + cmd_daemon_install/
uninstall/status/log + 8 private _daemon_* helpers) out of the airc
top-level into lib/airc_bash/cmd_daemon.sh, sourced via the same
lib-dir resolver as cmd_doctor.sh / cmd_connect.sh /
platform_adapters.sh.

  airc:                       5265 → 4834 lines (-431)
  lib/airc_bash/cmd_daemon.sh:  +461 (432 body + 29 header)

Behavior unchanged. Cross-references resolve at call-time:
  - cmd_daemon.sh calls airc top-level helpers (die, detect_platform)
  - airc top-level (monitor self-heal, line ~1292) calls
    _daemon_installed defined in cmd_daemon.sh

Verified:
  - bash -n on both files
  - airc daemon status — full plist/launchctl readout, log path correct

Stacks alongside #213 (cmd_connect extraction). Each PR independently
removes a major block from the bash monolith.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 28, 2026 15:52
@joelteply joelteply merged commit 8228920 into canary Apr 28, 2026
8 checks passed
@joelteply joelteply deleted the refactor/extract-cmd-daemon branch April 28, 2026 15:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extracts the cmd_daemon command family from the airc monolith into a dedicated bash module (lib/airc_bash/cmd_daemon.sh) and sources it via the existing lib-dir resolver, continuing the ongoing monolith split.

Changes:

  • Added lib/airc_bash/cmd_daemon.sh containing cmd_daemon* functions and _daemon_* helpers.
  • Removed the inlined cmd_daemon block from airc and replaced it with a guarded source of the new module.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
lib/airc_bash/cmd_daemon.sh New extracted module implementing airc daemon {install,status,uninstall,log} and platform-specific installers.
airc Replaces the inlined daemon implementation with sourcing of cmd_daemon.sh via _airc_lib_dir.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +191 to +196
_daemon_install_schtasks() {
# Windows daemon via HKCU Run-key (no admin; HKCU\...\Run is user-
# scope, so per-user autostart at logon without UAC). PRs #200/#202
# for the why; this function for the how.
local airc_bin="$1" scope="$2"
local entry_name="airc-monitor"
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name _daemon_install_schtasks is misleading: the implementation uses an HKCU Run-key launcher .bat, not Windows Scheduled Tasks. Renaming this helper (and its callsite/comment header) would make the platform behavior clearer for future maintainers.

Copilot uses AI. Check for mistakes.
Comment on lines +351 to +352
[ -f "$unit_path" ] && rm "$unit_path" && systemctl --user daemon-reload && echo " ✓ Removed $unit_path" \
|| echo " (no unit on disk)"
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This && ... || echo "(no unit on disk)" chain reports "no unit" for any failure in the removal path (e.g. systemctl --user daemon-reload failing), not just when the unit file is absent. Consider separating the checks so errors are surfaced accurately and the message reflects the real failure mode.

Suggested change
[ -f "$unit_path" ] && rm "$unit_path" && systemctl --user daemon-reload && echo " ✓ Removed $unit_path" \
|| echo " (no unit on disk)"
if [ -f "$unit_path" ]; then
if rm "$unit_path" && systemctl --user daemon-reload; then
echo " ✓ Removed $unit_path"
else
echo " Failed to remove $unit_path or reload systemd user units"
return 1
fi
else
echo " (no unit on disk)"
fi

Copilot uses AI. Check for mistakes.
echo " Plist: $plist_path"
# launchctl print returns rich state; grep the key fields.
local state; state=$(launchctl print "gui/$(id -u)/com.cambriantech.airc" 2>/dev/null \
| grep -E 'state =|pid =|last exit code' | head -3)
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In strict mode (set -euo pipefail), this command substitution will cause airc daemon status to exit when the job isn't loaded (e.g. launchctl print fails or grep finds no matches). Consider making the pipeline non-fatal (e.g. tolerate non-zero from launchctl print/grep) so the subsequent if [ -n "$state" ] branch can run as intended.

Suggested change
| grep -E 'state =|pid =|last exit code' | head -3)
| grep -E 'state =|pid =|last exit code' | head -3 || true)

Copilot uses AI. Check for mistakes.
Comment on lines +406 to +407
local active; active=$(systemctl --user is-active airc.service 2>/dev/null)
local enabled; enabled=$(systemctl --user is-enabled airc.service 2>/dev/null)
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

systemctl is-active/is-enabled return non-zero for common states like inactive/disabled. Under set -e, these assignments will terminate airc daemon status instead of printing the state. Capture the output while tolerating non-zero exit codes and defaulting to a sensible value when the command fails.

Suggested change
local active; active=$(systemctl --user is-active airc.service 2>/dev/null)
local enabled; enabled=$(systemctl --user is-enabled airc.service 2>/dev/null)
local active; active="$(systemctl --user is-active airc.service 2>/dev/null || true)"
[ -n "$active" ] || active="unknown"
local enabled; enabled="$(systemctl --user is-enabled airc.service 2>/dev/null || true)"
[ -n "$enabled" ] || enabled="unknown"

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +50
# Scope: defaults to the GLOBAL scope ($HOME/.airc), since the daemon is the
# user's "always-on" mesh presence — not tied to a specific project dir. If
# the user wants a per-project always-on daemon, they pass AIRC_HOME=<dir>
# in the environment when running install (and the generated unit/plist
# will carry that scope).
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header docs say the daemon scope defaults to $HOME/.airc, but _daemon_scope (and detect_scope in airc) default to $(pwd -P)/.airc. Please align the comment with the actual behavior (or change _daemon_scope if the intended default really is global).

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +128
# they call this helper. Pass the platform-specific lead line as $1 and
# any optional trailing note as $2 (heredoc-style multi-line OK).
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment describes _daemon_install_done's parameters as $1 lead line and $2 optional note, but the function signature is (lead, scope, note) where $2 is the scope path and $3 is the optional note. Update the comment to match the actual parameters to avoid misuses in future edits.

Suggested change
# they call this helper. Pass the platform-specific lead line as $1 and
# any optional trailing note as $2 (heredoc-style multi-line OK).
# they call this helper. Pass the platform-specific lead line as $1,
# the scope path as $2, and any optional trailing note as $3
# (heredoc-style multi-line OK).

Copilot uses AI. Check for mistakes.
Comment on lines +206 to +209
# Convert paths to Windows form; cmd.exe can't read /c/Users/... .
local airc_bin_win; airc_bin_win=$(_to_win_path "$airc_bin")
local scope_win; scope_win=$(_to_win_path "$scope")

Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

airc_bin_win is computed but never used. Dropping it (or using it in the generated launcher) would reduce dead code and avoid confusion about which path form is expected.

Suggested change
# Convert paths to Windows form; cmd.exe can't read /c/Users/... .
local airc_bin_win; airc_bin_win=$(_to_win_path "$airc_bin")
local scope_win; scope_win=$(_to_win_path "$scope")

Copilot uses AI. Check for mistakes.
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.

2 participants