ScuttleBot
fix: disable PTY→IRC mirroring — session file is the clean path PTY output contains terminal UI artifacts (spinners, partial line renders, ANSI fragments, bracketed paste mode) that are too noisy for IRC. The session file mirror already handles all structured output cleanly. PTY mirror is kept for busy signal detection and dedup only — no IRC posting.
Commit
4f3dcfeb8c16cf1edf949e22c514d9e423334006a1d9f8f9acd3889a81d82dc0
Parent
46ba17d882f9a55…
3 files changed
+5
-2
+1
-1
+1
-1
+5
-2
| --- cmd/claude-relay/main.go | ||
| +++ cmd/claude-relay/main.go | ||
| @@ -212,15 +212,18 @@ | ||
| 212 | 212 | "SCUTTLEBOT_HOOKS_ENABLED="+boolString(cfg.HooksEnabled), |
| 213 | 213 | "SCUTTLEBOT_SESSION_ID="+cfg.SessionID, |
| 214 | 214 | "SCUTTLEBOT_NICK="+cfg.Nick, |
| 215 | 215 | "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive), |
| 216 | 216 | ) |
| 217 | - // Create PTY mirror early so session file loop can dedup against it. | |
| 217 | + // PTY mirror: only used for busy signal detection and dedup, NOT for | |
| 218 | + // posting to IRC. The session file mirror handles all IRC output with | |
| 219 | + // clean structured data. PTY output is too noisy (spinners, partial | |
| 220 | + // renders, ANSI fragments) for direct IRC posting. | |
| 218 | 221 | var ptyMirror *relaymirror.PTYMirror |
| 219 | 222 | if relayActive { |
| 220 | 223 | ptyMirror = relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 221 | - go func() { _ = relay.Post(ctx, line) }() | |
| 224 | + // no-op: session file mirror handles IRC output | |
| 222 | 225 | }) |
| 223 | 226 | go mirrorSessionLoop(ctx, relay, cfg, startedAt, ptyMirror) |
| 224 | 227 | go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval) |
| 225 | 228 | } |
| 226 | 229 | |
| 227 | 230 |
| --- cmd/claude-relay/main.go | |
| +++ cmd/claude-relay/main.go | |
| @@ -212,15 +212,18 @@ | |
| 212 | "SCUTTLEBOT_HOOKS_ENABLED="+boolString(cfg.HooksEnabled), |
| 213 | "SCUTTLEBOT_SESSION_ID="+cfg.SessionID, |
| 214 | "SCUTTLEBOT_NICK="+cfg.Nick, |
| 215 | "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive), |
| 216 | ) |
| 217 | // Create PTY mirror early so session file loop can dedup against it. |
| 218 | var ptyMirror *relaymirror.PTYMirror |
| 219 | if relayActive { |
| 220 | ptyMirror = relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 221 | go func() { _ = relay.Post(ctx, line) }() |
| 222 | }) |
| 223 | go mirrorSessionLoop(ctx, relay, cfg, startedAt, ptyMirror) |
| 224 | go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval) |
| 225 | } |
| 226 | |
| 227 |
| --- cmd/claude-relay/main.go | |
| +++ cmd/claude-relay/main.go | |
| @@ -212,15 +212,18 @@ | |
| 212 | "SCUTTLEBOT_HOOKS_ENABLED="+boolString(cfg.HooksEnabled), |
| 213 | "SCUTTLEBOT_SESSION_ID="+cfg.SessionID, |
| 214 | "SCUTTLEBOT_NICK="+cfg.Nick, |
| 215 | "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive), |
| 216 | ) |
| 217 | // PTY mirror: only used for busy signal detection and dedup, NOT for |
| 218 | // posting to IRC. The session file mirror handles all IRC output with |
| 219 | // clean structured data. PTY output is too noisy (spinners, partial |
| 220 | // renders, ANSI fragments) for direct IRC posting. |
| 221 | var ptyMirror *relaymirror.PTYMirror |
| 222 | if relayActive { |
| 223 | ptyMirror = relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 224 | // no-op: session file mirror handles IRC output |
| 225 | }) |
| 226 | go mirrorSessionLoop(ctx, relay, cfg, startedAt, ptyMirror) |
| 227 | go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval) |
| 228 | } |
| 229 | |
| 230 |
+1
-1
| --- cmd/codex-relay/main.go | ||
| +++ cmd/codex-relay/main.go | ||
| @@ -231,11 +231,11 @@ | ||
| 231 | 231 | "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive), |
| 232 | 232 | ) |
| 233 | 233 | var ptyMirror *relaymirror.PTYMirror |
| 234 | 234 | if relayActive { |
| 235 | 235 | ptyMirror = relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 236 | - go func() { _ = relay.Post(ctx, line) }() | |
| 236 | + // no-op: session file mirror handles IRC output | |
| 237 | 237 | }) |
| 238 | 238 | go mirrorSessionLoop(ctx, relay, cfg, startedAt, preExisting, ptyMirror) |
| 239 | 239 | go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval) |
| 240 | 240 | } |
| 241 | 241 | |
| 242 | 242 |
| --- cmd/codex-relay/main.go | |
| +++ cmd/codex-relay/main.go | |
| @@ -231,11 +231,11 @@ | |
| 231 | "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive), |
| 232 | ) |
| 233 | var ptyMirror *relaymirror.PTYMirror |
| 234 | if relayActive { |
| 235 | ptyMirror = relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 236 | go func() { _ = relay.Post(ctx, line) }() |
| 237 | }) |
| 238 | go mirrorSessionLoop(ctx, relay, cfg, startedAt, preExisting, ptyMirror) |
| 239 | go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval) |
| 240 | } |
| 241 | |
| 242 |
| --- cmd/codex-relay/main.go | |
| +++ cmd/codex-relay/main.go | |
| @@ -231,11 +231,11 @@ | |
| 231 | "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive), |
| 232 | ) |
| 233 | var ptyMirror *relaymirror.PTYMirror |
| 234 | if relayActive { |
| 235 | ptyMirror = relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 236 | // no-op: session file mirror handles IRC output |
| 237 | }) |
| 238 | go mirrorSessionLoop(ctx, relay, cfg, startedAt, preExisting, ptyMirror) |
| 239 | go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval) |
| 240 | } |
| 241 | |
| 242 |
+1
-1
| --- cmd/gemini-relay/main.go | ||
| +++ cmd/gemini-relay/main.go | ||
| @@ -222,11 +222,11 @@ | ||
| 222 | 222 | _, _ = io.Copy(ptmx, os.Stdin) |
| 223 | 223 | }() |
| 224 | 224 | // Dual-path mirroring: PTY for real-time text + session file for metadata. |
| 225 | 225 | ptyMirror := relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 226 | 226 | if relayActive { |
| 227 | - go func() { _ = relay.Post(ctx, line) }() | |
| 227 | + // no-op: session file mirror handles IRC output | |
| 228 | 228 | } |
| 229 | 229 | }) |
| 230 | 230 | ptyMirror.BusyCallback = func(now time.Time) { |
| 231 | 231 | state.mu.Lock() |
| 232 | 232 | state.lastBusy = now |
| 233 | 233 |
| --- cmd/gemini-relay/main.go | |
| +++ cmd/gemini-relay/main.go | |
| @@ -222,11 +222,11 @@ | |
| 222 | _, _ = io.Copy(ptmx, os.Stdin) |
| 223 | }() |
| 224 | // Dual-path mirroring: PTY for real-time text + session file for metadata. |
| 225 | ptyMirror := relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 226 | if relayActive { |
| 227 | go func() { _ = relay.Post(ctx, line) }() |
| 228 | } |
| 229 | }) |
| 230 | ptyMirror.BusyCallback = func(now time.Time) { |
| 231 | state.mu.Lock() |
| 232 | state.lastBusy = now |
| 233 |
| --- cmd/gemini-relay/main.go | |
| +++ cmd/gemini-relay/main.go | |
| @@ -222,11 +222,11 @@ | |
| 222 | _, _ = io.Copy(ptmx, os.Stdin) |
| 223 | }() |
| 224 | // Dual-path mirroring: PTY for real-time text + session file for metadata. |
| 225 | ptyMirror := relaymirror.NewPTYMirror(defaultMirrorLineMax, 500*time.Millisecond, func(line string) { |
| 226 | if relayActive { |
| 227 | // no-op: session file mirror handles IRC output |
| 228 | } |
| 229 | }) |
| 230 | ptyMirror.BusyCallback = func(now time.Time) { |
| 231 | state.mu.Lock() |
| 232 | state.lastBusy = now |
| 233 |