Files
clide/lib/builtin/claude/src/tmux_session.dart
T
jpmschweitzerandClaude f0bb2ffcce format the tree to current dart format spec
Mechanical `dart format` sweep across files that drifted from the
formatter's output (mostly trailing-comma and line-wrap differences
from a Dart SDK / formatter version bump). No semantic changes.

Caught because the pre-push gate now actually fires.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 22:28:51 +02:00

57 lines
2.1 KiB
Dart

/// tmux server interactions for Claude panes (D-41 lifecycle).
///
/// `pane.close` only kills the ptyc-spawned tmux *client*; tmux is
/// client/server, so the server-side session keeps running after the
/// client disconnects. To honour D-41 ("closing a secondary kills that
/// tmux session" + "secondary numbering resets between clide runs"),
/// we need explicit `tmux kill-session` calls — that's what lives here.
library;
import 'dart:io';
/// Override-able runner so tests don't shell out for real.
typedef TmuxRunner = Future<ProcessResult> Function(List<String> args);
TmuxRunner tmuxRunner = _defaultRunner;
Future<ProcessResult> _defaultRunner(List<String> args) => Process.run('tmux', args);
const _socket = ['-L', 'clide'];
/// Kill the named tmux session on the clide socket. No-op if the
/// session does not exist (kill-session exits non-zero — we ignore it).
Future<void> killSession(String name) async {
await tmuxRunner([..._socket, 'kill-session', '-t', name]);
}
/// Return the names of all sessions currently alive on the clide
/// socket. Empty list if the server is not running.
Future<List<String>> listClideSessions() async {
final r = await tmuxRunner([..._socket, 'list-sessions', '-F', '#{session_name}']);
if (r.exitCode != 0) return const [];
return (r.stdout as String).split('\n').map((s) => s.trim()).where((s) => s.isNotEmpty).toList();
}
/// Kill every secondary clide-claude session whose name begins with
/// [primaryName] and ends with `-<digits>`. Leaves the primary itself
/// alive (D-41).
Future<void> reapSecondaries(String primaryName) async {
final pattern = RegExp('^${RegExp.escape(primaryName)}-\\d+\$');
for (final s in await listClideSessions()) {
if (pattern.hasMatch(s)) {
await killSession(s);
}
}
}
/// Kill every clide-claude session for [primaryName], including the
/// primary itself. Used by the explicit `claude.kill-all-sessions`
/// command when the user wants a hard reset.
Future<void> killAllForRepo(String primaryName) async {
for (final s in await listClideSessions()) {
if (s == primaryName || s.startsWith('$primaryName-')) {
await killSession(s);
}
}
}