refactor(airc-bash): final cmd_X sweep — Phase 3 file split COMPLETE#221
refactor(airc-bash): final cmd_X sweep — Phase 3 file split COMPLETE#221
Conversation
Pulls the three remaining cmd_X groups out of airc top-level:
- cmd_reminder → lib/airc_bash/cmd_reminder.sh (32 → 46 lines w/ header)
- cmd_rename → lib/airc_bash/cmd_rename.sh (101 → 121)
- cmd_update + cmd_channel + cmd_version
→ lib/airc_bash/cmd_update.sh (130 → 148)
airc: 1898 → 1663 lines (-235)
## Phase 3 file split — final summary
airc top-level: 5265 → 1663 lines (-3602, -68%)
lib/airc_bash/: 0 → 4569 lines across 13 files
cmd_connect.sh 1379 (join/pair/host orchestrator)
cmd_daemon.sh 461 (autostart family + helpers)
cmd_rooms.sh 441 (channel/peer cluster: rooms/part/invite/send-file/peers)
cmd_doctor.sh 441 (env health + connect preflight)
cmd_identity.sh 448 (presence: away/identity/whois + helpers)
cmd_send.sh 383 (outbound: send + ping)
cmd_teardown.sh 273 (leave/cleanup: teardown + disconnect)
platform_adapters.sh 176 (proc_/port_/file_ adapters)
cmd_status.sh 170 (introspection: status + logs)
cmd_update.sh 148 (release info: update/channel/version)
cmd_rename.sh 121 (identity name change w/ multi-scope propagation)
cmd_kick.sh 82 (host-only peer eviction)
cmd_reminder.sh 46 (idle-nudge cadence)
What's left in airc top-level (1663 lines):
- bootstrap (lib-dir resolver, env, source-blocks)
- helpers (die, ensure_init, get_/set_config_val, resolve_name,
relay_ssh, get_host, monitor + monitor self-heal, _hash, …)
- dispatch case + help text
Verified: full integration suite (tabs scenario) passing 19/0.
Closes the structural decomposition Joel called for (2026-04-27):
"shell scripts are like classes; never ever again make 5000 line
dumbass designs." Future passes should decompose cmd_connect.sh
internally (host-mode vs joiner-mode vs heartbeat are clearly
separable) — the 1379-line connect file is now the single largest
remaining block. But the bash monolith itself is gone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Final Phase 3 of the bash monolith split: moves the remaining cmd_* groups (reminder, rename, update/channel/version) out of the top-level airc script into sourced modules under lib/airc_bash/, keeping airc focused on bootstrap/helpers/dispatch.
Changes:
- Added new sourced bash modules:
cmd_reminder.sh,cmd_rename.sh,cmd_update.sh. - Removed inline implementations of those commands from
aircand replaced them with lib-dir-resolvedsourceblocks. - Kept dispatch behavior the same while reducing the top-level script size.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
lib/airc_bash/cmd_update.sh |
New module for update/channel/version command cluster. |
lib/airc_bash/cmd_rename.sh |
New module for rename + multi-scope propagation logic. |
lib/airc_bash/cmd_reminder.sh |
New module for reminder interval control. |
airc |
Removes inline cmd implementations and sources the new modules via _airc_lib_dir. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return 0 | ||
| fi | ||
|
|
||
| echo "$target" > "$channel_file" |
There was a problem hiding this comment.
cmd_channel writes the provided channel name straight to $dir/.channel without trimming/validation or checking that $dir exists. With set -e, a missing directory will terminate the whole CLI via redirection failure, and whitespace/newlines in the name will later be stripped by cmd_update (surprising mismatch). Consider validating the name (non-empty, no leading -, no whitespace), and fail via die with a clear message if the install dir or .git checkout isn’t present.
| echo "$target" > "$channel_file" | |
| [ -d "$dir" ] || die "Install directory not found: $dir" | |
| [ -d "$dir/.git" ] || die "Install directory is not a git checkout: $dir" | |
| case "$target" in | |
| "" ) die "Usage: airc channel <name>" ;; | |
| -* ) die "Invalid channel name '$target': channel names must not start with '-'" ;; | |
| *[[:space:]]* ) die "Invalid channel name '$target': channel names must not contain whitespace" ;; | |
| esac | |
| printf '%s\n' "$target" > "$channel_file" |
| ;; | ||
| --canary) requested_channel="canary"; shift ;; | ||
| --main) requested_channel="main"; shift ;; | ||
| *) shift ;; |
There was a problem hiding this comment.
The arg parser silently ignores any unrecognized arguments (*) shift ;;). That can hide typos (e.g., airc update --chanel canary) and makes it hard to detect user error. Since cmd_update doesn’t accept positional args, consider dieing on any unknown token (or at least any unknown -* flag) with a usage hint.
| *) shift ;; | |
| *) | |
| die "Unknown argument: $1. Usage: airc update [--channel <name> | --canary | --main]" | |
| ;; |
| if [ "$current_branch" != "$channel" ]; then | ||
| git -C "$dir" fetch --quiet origin "$channel" 2>/dev/null \ | ||
| || die "Channel '$channel' not found on origin. Try: airc channel (to see options)." | ||
| git -C "$dir" checkout -q "$channel" 2>/dev/null \ | ||
| || git -C "$dir" checkout -q -B "$channel" "origin/$channel" 2>/dev/null \ | ||
| || die "Failed to checkout '$channel'. Resolve manually in $dir." |
There was a problem hiding this comment.
git checkout -q "$channel" can misinterpret a channel name that begins with - as an option. Since channel is user-controlled (CLI arg / .channel file), add a -- end-of-options marker (and ideally validate/sanitize the channel string, e.g. git check-ref-format --branch, or reject leading -) before passing it to git.
Summary
The final structural sweep — pulls the last three cmd_X groups out of the airc top-level:
```
airc top-level: 1898 → 1663 lines (-235)
```
Phase 3 file split — END STATE
```
lib/airc_bash/cmd_connect.sh 1379 (join/pair/host orchestrator)
lib/airc_bash/cmd_daemon.sh 461 (autostart family + helpers)
lib/airc_bash/cmd_rooms.sh 441 (channel/peer cluster)
lib/airc_bash/cmd_doctor.sh 441 (env health + connect preflight)
lib/airc_bash/cmd_identity.sh 448 (presence: away/identity/whois)
lib/airc_bash/cmd_send.sh 383 (outbound: send + ping)
lib/airc_bash/cmd_teardown.sh 273 (leave/cleanup)
lib/airc_bash/platform_adapters.sh 176 (proc_/port_/file_ adapters)
lib/airc_bash/cmd_status.sh 170 (introspection)
lib/airc_bash/cmd_update.sh 148 (release info)
lib/airc_bash/cmd_rename.sh 121 (name change w/ multi-scope propagation)
lib/airc_bash/cmd_kick.sh 82 (host-only peer eviction)
lib/airc_bash/cmd_reminder.sh 46 (idle-nudge cadence)
```
What remains in airc top-level (1663 lines):
Verification
Why this matters
Closes the structural decomposition Joel called for 2026-04-27:
The bash monolith is gone. Future passes should decompose `cmd_connect.sh` internally (host-mode vs joiner-mode vs heartbeat are clearly separable) — the 1379-line connect file is now the single largest remaining block. But cmd_connect.sh's internal split is a different shape of work (intra-cmd refactor) than this Phase 3 file split (file-per-cmd).
Stack
Stacks alongside merged #213, #214, #215, #216, #217, #218, #219, #220.
🤖 Generated with Claude Code