home / skills / terrylica / cc-skills / bot-process-control

This skill manages the Gmail Commander bot daemon and digest schedules, enabling start, stop, restart, status, and log access.

npx playbooks add skill terrylica/cc-skills --skill bot-process-control

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.4 KB
---
name: bot-process-control
description: Gmail Commander daemon lifecycle - start, stop, restart, status, logs, launchd plist management. TRIGGERS - bot start, bot stop, bot restart, bot status, bot logs, launchd, daemon, process control, gmail-commander service.
allowed-tools: Read, Bash, Grep, Glob
---

# Bot Process Control

Manage the Gmail Commander bot daemon and scheduled digest via launchd.

## Mandatory Preflight

### Step 1: Check Current Process Status

```bash
echo "=== Gmail Commander Processes ==="
pgrep -fl "gmail-commander" 2>/dev/null || echo "No processes found"

echo ""
echo "=== launchd Status ==="
launchctl list | grep gmail-commander 2>/dev/null || echo "No launchd jobs"

echo ""
echo "=== PID Files ==="
cat /tmp/gmail-commander-bot.pid 2>/dev/null && echo " (bot)" || echo "No bot PID file"
cat /tmp/gmail-digest.pid 2>/dev/null && echo " (digest)" || echo "No digest PID file"
```

## Two Services

| Service    | Type          | Trigger                    | PID File                     |
| ---------- | ------------- | -------------------------- | ---------------------------- |
| Bot Daemon | KeepAlive     | Always-on (grammY polling) | /tmp/gmail-commander-bot.pid |
| Digest     | StartInterval | Every 6 hours (21600s)     | /tmp/gmail-digest.pid        |

## launchd Plist Templates

### Bot Daemon — `com.terryli.gmail-commander-bot.plist`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.terryli.gmail-commander-bot</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/terryli/own/amonic/bin/gmail-commander-bot</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <dict>
        <key>NetworkState</key>
        <true/>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/terryli/own/amonic/logs/bot-stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/terryli/own/amonic/logs/bot-stderr.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/Users/terryli/.local/share/mise/shims:/usr/local/bin:/usr/bin:/bin</string>
    </dict>
    <key>ThrottleInterval</key>
    <integer>10</integer>
</dict>
</plist>
```

### Scheduled Digest — `com.terryli.gmail-commander-digest.plist`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.terryli.gmail-commander-digest</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/terryli/own/amonic/bin/gmail-commander-digest</string>
    </array>
    <key>StartInterval</key>
    <integer>21600</integer>
    <key>StandardOutPath</key>
    <string>/Users/terryli/own/amonic/logs/digest-stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/terryli/own/amonic/logs/digest-stderr.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/Users/terryli/.local/share/mise/shims:/usr/local/bin:/usr/bin:/bin</string>
    </dict>
</dict>
</plist>
```

## Quick Operations

### Start Bot

```bash
launchctl load ~/Library/LaunchAgents/com.terryli.gmail-commander-bot.plist
```

### Stop Bot

```bash
launchctl unload ~/Library/LaunchAgents/com.terryli.gmail-commander-bot.plist
```

### Restart Bot

```bash
launchctl unload ~/Library/LaunchAgents/com.terryli.gmail-commander-bot.plist
launchctl load ~/Library/LaunchAgents/com.terryli.gmail-commander-bot.plist
```

### Force Kill (Emergency)

```bash
pkill -f "gmail-commander.*bot.ts"
rm -f /tmp/gmail-commander-bot.pid
```

### View Logs

```bash
# Recent bot output
tail -50 ~/own/amonic/logs/bot-stderr.log

# Recent digest output
tail -50 ~/own/amonic/logs/digest-stderr.log

# Audit log (NDJSON)
cat ~/own/amonic/logs/audit/$(date +%Y-%m-%d).ndjson | jq .
```

## System Resources (Expected)

- **Memory**: ~20-30 MB RSS (Bun runtime + grammY)
- **CPU**: Negligible (idle polling, wakes on message)
- **Network**: Minimal (single long-poll connection to Telegram API)
- **Disk**: ~1 MB/day audit logs (14-day rotation)

## Post-Change Checklist

- [ ] YAML frontmatter valid (no colons in description)
- [ ] Trigger keywords current
- [ ] Path patterns use $HOME not hardcoded paths
- [ ] launchd plist templates match actual launcher scripts

Overview

This skill manages the Gmail Commander daemon and its scheduled digest on macOS using launchd. It provides lifecycle commands to start, stop, restart, check status, view logs, and manage launchd plist files for both the always-on bot and the periodic digest. It focuses on safe process control, PID handling, and simple troubleshooting steps.

How this skill works

The skill inspects running processes, PID files, and launchd job listings to determine service state. It can load/unload the two launchd plists (bot daemon and scheduled digest), tail relevant logs, and perform emergency force-kill and PID cleanup when needed. It also includes plist templates and resource expectations to help validate deployments.

When to use it

  • Deploy or update the Gmail Commander bot or digest on a macOS host
  • Start, stop, or restart the daemon after code changes or configuration edits
  • Check process and launchd status during incident triage
  • Inspect recent stderr/stdout output and audit NDJSON logs for debugging
  • Perform emergency shutdown and PID cleanup when the process is unresponsive

Best practices

  • Use launchd plists with $HOME-based paths instead of hardcoded absolute paths to support multiple users
  • Verify PID files in /tmp before killing processes; remove stale PID files after confirming process termination
  • Keep separate plists for the bot (KeepAlive) and digest (StartInterval) to control lifecycle independently
  • Redirect stdout/stderr to dedicated log files and rotate logs (audit NDJSON) to avoid disk growth
  • Confirm environment PATH in plist EnvironmentVariables to match runtime dependencies (bun, node, etc.)

Example use cases

  • Install and enable com.terryli.gmail-commander-bot.plist to run the grammY-based bot as a persistent service
  • Deploy com.terryli.gmail-commander-digest.plist for a 6-hour scheduled digest run
  • Restart the bot after pulling code changes and validate by tailing bot-stderr.log
  • During an outage, run pgrep and check /tmp/*.pid to locate stuck processes, then pkill and remove stale PID files
  • Audit daily activity by parsing the NDJSON audit log for a specific date

FAQ

How do I check whether the bot or digest is running?

Run pgrep -fl gmail-commander, check launchctl list for gmail-commander jobs, and inspect /tmp/gmail-commander-bot.pid and /tmp/gmail-digest.pid for active PID files.

What if launchctl load fails after plist changes?

Unload the job, validate plist syntax and EnvironmentVariables, replace hardcoded paths with $HOME, then load again. Tail stderr logs for immediate errors.

When should I force kill the bot?

Only if the bot is unresponsive to unload/load and pgrep shows a hung process. Use pkill -f with the process pattern and remove the corresponding /tmp PID file after confirming termination.