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,
|
makeEnable,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
# Working directory the always-on session is pinned to. Ad-hoc sessions in
|
# Directories that each get an always-on Remote Control session, so new
|
||||||
# other directories are handled by the `tmclaude` shell function instead.
|
# conversations can be started in any of them from claude.ai/code or the
|
||||||
workingDirectory = "/srv/dotfiles";
|
# 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
|
# Only run a session for directories that actually exist on this host, so the
|
||||||
# server (independent of the interactive one). Attach locally with:
|
# same module can be enabled across machines where some projects live at
|
||||||
# tmux -L claude-rc attach -t claude-rc
|
# different paths (or not at all). Relies on impure evaluation, which the
|
||||||
socket = "claude-rc";
|
# `just switch` workflow already uses (`nixos-rebuild ... --impure`).
|
||||||
sessionName = "claude-rc";
|
directories = builtins.filter builtins.pathExists candidateDirectories;
|
||||||
|
|
||||||
# Name the session registers under for native Remote Control (phone/web).
|
|
||||||
remoteName = config.networking.hostName;
|
|
||||||
|
|
||||||
# claude shells out to these for its tools; give the service a clean PATH.
|
# claude shells out to these for its tools; give the service a clean PATH.
|
||||||
servicePath = lib.makeBinPath (with pkgs; [
|
servicePath = lib.makeBinPath (with pkgs; [
|
||||||
@@ -34,31 +42,53 @@
|
|||||||
ripgrep
|
ripgrep
|
||||||
zsh
|
zsh
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
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 (${dir})";
|
||||||
|
After = ["network.target"];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
# tmux new-session -d daemonizes the server and returns.
|
||||||
|
Type = "forking";
|
||||||
|
Environment = ["PATH=${servicePath}"];
|
||||||
|
ExecStart = lib.concatStringsSep " " [
|
||||||
|
"${pkgs.tmux}/bin/tmux -L ${socket} new-session -d"
|
||||||
|
"-s ${sessionName} -c ${dir}"
|
||||||
|
"${pkgs.claude-code}/bin/claude --remote-control ${remoteName} --dangerously-skip-permissions"
|
||||||
|
];
|
||||||
|
ExecStop = "${pkgs.tmux}/bin/tmux -L ${socket} kill-server";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 5;
|
||||||
|
};
|
||||||
|
Install.WantedBy = ["default.target"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkAlias = dir: let
|
||||||
|
name = mkName dir;
|
||||||
|
in {
|
||||||
|
name = "claude-rc-attach-${name}";
|
||||||
|
value = "tmux -L claude-rc-${name} attach -t claude-rc-${name}";
|
||||||
|
};
|
||||||
in
|
in
|
||||||
makeEnable config "myModules.claudeRemoteControl" false {
|
makeEnable config "myModules.claudeRemoteControl" false {
|
||||||
home-manager.users.imalison = {
|
home-manager.users.imalison = {
|
||||||
systemd.user.services.claude-remote-control = {
|
systemd.user.services = builtins.listToAttrs (map mkService directories);
|
||||||
Unit = {
|
|
||||||
Description = "Claude Code remote-control session";
|
|
||||||
After = ["network.target"];
|
|
||||||
};
|
|
||||||
Service = {
|
|
||||||
# tmux new-session -d daemonizes the server and returns.
|
|
||||||
Type = "forking";
|
|
||||||
Environment = ["PATH=${servicePath}"];
|
|
||||||
ExecStart = lib.concatStringsSep " " [
|
|
||||||
"${pkgs.tmux}/bin/tmux -L ${socket} new-session -d"
|
|
||||||
"-s ${sessionName} -c ${workingDirectory}"
|
|
||||||
"${pkgs.claude-code}/bin/claude --remote-control ${remoteName} --dangerously-skip-permissions"
|
|
||||||
];
|
|
||||||
ExecStop = "${pkgs.tmux}/bin/tmux -L ${socket} kill-server";
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = 5;
|
|
||||||
};
|
|
||||||
Install.WantedBy = ["default.target"];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Convenience: attach to the always-on session from any directory.
|
# Convenience: attach to any always-on session from any directory.
|
||||||
home.shellAliases.claude-rc-attach = "tmux -L ${socket} attach -t ${sessionName}";
|
home.shellAliases = builtins.listToAttrs (map mkAlias directories);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
features.full.enable = true;
|
features.full.enable = true;
|
||||||
|
myModules.claudeRemoteControl.enable = true;
|
||||||
myModules.games.enable = lib.mkForce true;
|
myModules.games.enable = lib.mkForce true;
|
||||||
myModules.kubelet.enable = false;
|
myModules.kubelet.enable = false;
|
||||||
myModules.nvidia.enable = true;
|
myModules.nvidia.enable = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user