claude-remote-control: run a session per project directory
Generalize the always-on Remote Control module from a single session pinned to /srv/dotfiles into one systemd user service per directory, so new conversations can be started remotely (claude.ai/code / mobile) in any of them without running `claude rc` by hand. Each directory gets its own tmux socket, session, and Remote Control name derived from its base name, plus a `claude-rc-attach-<name>` alias. Directories that don't exist on a given host are skipped (impure pathExists check) so the module can be shared across machines. Initial set: dotfiles, subtr-actor, rocket-sense, mova. Enabled on both railbird-sf and ryzen-shine. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,18 +5,26 @@
|
||||
makeEnable,
|
||||
...
|
||||
}: let
|
||||
# Working directory the always-on session is pinned to. Ad-hoc sessions in
|
||||
# other directories are handled by the `tmclaude` shell function instead.
|
||||
workingDirectory = "/srv/dotfiles";
|
||||
# Directories that each get an always-on Remote Control session, so new
|
||||
# conversations can be started in any of them from claude.ai/code or the
|
||||
# Claude mobile app (without having to run `claude rc` by hand first).
|
||||
#
|
||||
# Each directory gets its own systemd user service, tmux server (dedicated
|
||||
# `-L` socket) and Remote Control session name derived from the directory's
|
||||
# base name. Attach locally with the generated `claude-rc-attach-<name>`
|
||||
# alias, e.g. `claude-rc-attach-dotfiles`.
|
||||
candidateDirectories = [
|
||||
"/srv/dotfiles"
|
||||
"/home/imalison/Projects/subtr-actor"
|
||||
"/home/imalison/Projects/rocket-sense"
|
||||
"/home/imalison/code/mova"
|
||||
];
|
||||
|
||||
# Dedicated tmux socket + session name so the service owns its own tmux
|
||||
# server (independent of the interactive one). Attach locally with:
|
||||
# tmux -L claude-rc attach -t claude-rc
|
||||
socket = "claude-rc";
|
||||
sessionName = "claude-rc";
|
||||
|
||||
# Name the session registers under for native Remote Control (phone/web).
|
||||
remoteName = config.networking.hostName;
|
||||
# Only run a session for directories that actually exist on this host, so the
|
||||
# same module can be enabled across machines where some projects live at
|
||||
# different paths (or not at all). Relies on impure evaluation, which the
|
||||
# `just switch` workflow already uses (`nixos-rebuild ... --impure`).
|
||||
directories = builtins.filter builtins.pathExists candidateDirectories;
|
||||
|
||||
# claude shells out to these for its tools; give the service a clean PATH.
|
||||
servicePath = lib.makeBinPath (with pkgs; [
|
||||
@@ -34,12 +42,22 @@
|
||||
ripgrep
|
||||
zsh
|
||||
]);
|
||||
in
|
||||
makeEnable config "myModules.claudeRemoteControl" false {
|
||||
home-manager.users.imalison = {
|
||||
systemd.user.services.claude-remote-control = {
|
||||
|
||||
mkName = dir: baseNameOf dir;
|
||||
|
||||
mkService = dir: let
|
||||
name = mkName dir;
|
||||
# Dedicated tmux socket + session name so each service owns its own tmux
|
||||
# server, independent of the interactive one and of every other session.
|
||||
socket = "claude-rc-${name}";
|
||||
sessionName = "claude-rc-${name}";
|
||||
# Name the session registers under for native Remote Control (phone/web).
|
||||
remoteName = "${config.networking.hostName}-${name}";
|
||||
in {
|
||||
name = "claude-remote-control-${name}";
|
||||
value = {
|
||||
Unit = {
|
||||
Description = "Claude Code remote-control session";
|
||||
Description = "Claude Code remote-control session (${dir})";
|
||||
After = ["network.target"];
|
||||
};
|
||||
Service = {
|
||||
@@ -48,7 +66,7 @@ in
|
||||
Environment = ["PATH=${servicePath}"];
|
||||
ExecStart = lib.concatStringsSep " " [
|
||||
"${pkgs.tmux}/bin/tmux -L ${socket} new-session -d"
|
||||
"-s ${sessionName} -c ${workingDirectory}"
|
||||
"-s ${sessionName} -c ${dir}"
|
||||
"${pkgs.claude-code}/bin/claude --remote-control ${remoteName} --dangerously-skip-permissions"
|
||||
];
|
||||
ExecStop = "${pkgs.tmux}/bin/tmux -L ${socket} kill-server";
|
||||
@@ -57,8 +75,20 @@ in
|
||||
};
|
||||
Install.WantedBy = ["default.target"];
|
||||
};
|
||||
};
|
||||
|
||||
# Convenience: attach to the always-on session from any directory.
|
||||
home.shellAliases.claude-rc-attach = "tmux -L ${socket} attach -t ${sessionName}";
|
||||
mkAlias = dir: let
|
||||
name = mkName dir;
|
||||
in {
|
||||
name = "claude-rc-attach-${name}";
|
||||
value = "tmux -L claude-rc-${name} attach -t claude-rc-${name}";
|
||||
};
|
||||
in
|
||||
makeEnable config "myModules.claudeRemoteControl" false {
|
||||
home-manager.users.imalison = {
|
||||
systemd.user.services = builtins.listToAttrs (map mkService directories);
|
||||
|
||||
# Convenience: attach to any always-on session from any directory.
|
||||
home.shellAliases = builtins.listToAttrs (map mkAlias directories);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
];
|
||||
|
||||
features.full.enable = true;
|
||||
myModules.claudeRemoteControl.enable = true;
|
||||
myModules.games.enable = lib.mkForce true;
|
||||
myModules.kubelet.enable = false;
|
||||
myModules.nvidia.enable = true;
|
||||
|
||||
Reference in New Issue
Block a user