chore: drop claude-rc
No longer used. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,158 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Manage the live list of Claude Code Remote Control projects.
|
||||
# The projects file is the source of truth; sync reconciles systemd to match.
|
||||
set -euo pipefail
|
||||
|
||||
LIST="${CLAUDE_RC_LIST:-$HOME/.config/claude-rc/projects}"
|
||||
UNIT=claude-rc
|
||||
|
||||
die() { echo "claude-rc: $*" >&2; exit 1; }
|
||||
|
||||
# configured paths: strip comments, an optional "| Display Name" suffix,
|
||||
# surrounding space, and any trailing slash.
|
||||
wanted_paths() {
|
||||
[ -f "$LIST" ] || return 0
|
||||
sed -e 's/#.*//' -e 's/|.*//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' "$LIST" \
|
||||
| grep -v '^$' \
|
||||
| sed -e 's#/\+$##' \
|
||||
| sort -u
|
||||
}
|
||||
|
||||
# display name for a path, if the list defines one
|
||||
name_for() {
|
||||
[ -f "$LIST" ] || return 0
|
||||
awk -F'|' -v t="$1" '
|
||||
{ line=$0; sub(/#.*/,"",line)
|
||||
p=line; if (index(line,"|")) { p=substr(line,1,index(line,"|")-1) }
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",p); sub(/\/+$/,"",p)
|
||||
if (p == t && index(line,"|")) {
|
||||
n=substr(line,index(line,"|")+1)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",n)
|
||||
if (n != "") { print n; exit }
|
||||
}
|
||||
}' "$LIST" 2>/dev/null
|
||||
}
|
||||
|
||||
# instance names systemd should have enabled
|
||||
wanted_units() {
|
||||
wanted_paths | while read -r p; do
|
||||
printf '%s@%s.service\n' "$UNIT" "$(systemd-escape -p "$p")"
|
||||
done | sort -u
|
||||
}
|
||||
|
||||
# instance names systemd currently has
|
||||
current_units() {
|
||||
systemctl --user list-units --all --no-legend --plain "${UNIT}@*.service" 2>/dev/null \
|
||||
| awk '{print $1}' | sort -u
|
||||
}
|
||||
|
||||
cmd_sync() {
|
||||
local want have add rm
|
||||
want=$(wanted_units); have=$(current_units)
|
||||
add=$(comm -23 <(printf '%s\n' "$want") <(printf '%s\n' "$have") | grep -v '^$' || true)
|
||||
rm=$(comm -13 <(printf '%s\n' "$want") <(printf '%s\n' "$have") | grep -v '^$' || true)
|
||||
|
||||
if [ -n "$rm" ]; then
|
||||
while read -r u; do
|
||||
echo "- stop $u"
|
||||
systemctl --user disable --now "$u" >/dev/null 2>&1 || true
|
||||
done <<<"$rm"
|
||||
fi
|
||||
if [ -n "$add" ]; then
|
||||
while read -r u; do
|
||||
echo "+ start $u"
|
||||
systemctl --user enable --now "$u" >/dev/null 2>&1 \
|
||||
|| echo " FAILED: $u (see: claude-rc log $u)" >&2
|
||||
done <<<"$add"
|
||||
fi
|
||||
[ -z "$add$rm" ] && echo "in sync ($(wanted_paths | grep -c . || true) projects)"
|
||||
return 0
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local n=0
|
||||
printf '%-46s %-9s %s\n' PROJECT STATE GIT
|
||||
while read -r p; do
|
||||
n=$((n+1))
|
||||
local u state git
|
||||
u="${UNIT}@$(systemd-escape -p "$p").service"
|
||||
state=$(systemctl --user is-active "$u" 2>/dev/null || true)
|
||||
if [ ! -d "$p" ]; then git="MISSING DIR"
|
||||
elif git -C "$p" rev-parse --git-dir >/dev/null 2>&1; then git="worktree"
|
||||
else git="same-dir (not git)"; fi
|
||||
printf '%-46s %-9s %s\n' "${p/#$HOME/\~}" "$state" "$git"
|
||||
done < <(wanted_paths)
|
||||
[ "$n" = 0 ] && echo "(no projects configured; claude-rc add <path>)"
|
||||
return 0
|
||||
}
|
||||
|
||||
cmd_add() {
|
||||
local p="${1:-$PWD}"
|
||||
p=$(cd "$p" 2>/dev/null && pwd) || die "no such directory: ${1:-$PWD}"
|
||||
if wanted_paths | grep -qxF "$p"; then echo "already listed: $p"; return 0; fi
|
||||
# revive a commented-out entry if present, else append; keeps the file tidy
|
||||
# across add/rm cycles instead of accumulating dead duplicates.
|
||||
if awk -v t="$p" '
|
||||
{ l=$0; sub(/^[[:space:]]*#[[:space:]]*/,"",l); sub(/\/+$/,"",l)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",l)
|
||||
if ($0 ~ /^[[:space:]]*#/ && l == t) found=1 }
|
||||
END { exit !found }' "$LIST"; then
|
||||
awk -v t="$p" '
|
||||
{ l=$0; sub(/^[[:space:]]*#[[:space:]]*/,"",l); sub(/\/+$/,"",l)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",l)
|
||||
if (!done && $0 ~ /^[[:space:]]*#/ && l == t) { print t; done=1 }
|
||||
else print $0 }
|
||||
' "$LIST" > "$LIST.tmp" && mv "$LIST.tmp" "$LIST"
|
||||
else
|
||||
printf '%s\n' "$p" >> "$LIST"
|
||||
fi
|
||||
echo "added: $p"
|
||||
cmd_sync
|
||||
}
|
||||
|
||||
cmd_rm() {
|
||||
local p="${1:?usage: claude-rc rm <path>}"
|
||||
p=$(cd "$p" 2>/dev/null && pwd) || p="${p%/}"
|
||||
wanted_paths | grep -qxF "$p" || die "not listed: $p"
|
||||
# comment out rather than delete, so the entry is easy to restore.
|
||||
# awk, not sed: the path is data, so no regex metachar/delimiter escaping.
|
||||
awk -v target="$p" '
|
||||
{
|
||||
line = $0
|
||||
sub(/#.*/, "", line)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
|
||||
sub(/\/+$/, "", line)
|
||||
if (line != "" && line == target) print "# " $0
|
||||
else print $0
|
||||
}
|
||||
' "$LIST" > "$LIST.tmp" && mv "$LIST.tmp" "$LIST"
|
||||
echo "removed: $p"
|
||||
cmd_sync
|
||||
}
|
||||
|
||||
cmd_edit() { "${EDITOR:-vi}" "$LIST"; cmd_sync; }
|
||||
cmd_log() {
|
||||
local p="${1:?usage: claude-rc log <path>}"
|
||||
p=$(cd "$p" 2>/dev/null && pwd) || die "no such directory: $p"
|
||||
journalctl --user -u "${UNIT}@$(systemd-escape -p "$p").service" -n 40 --no-pager
|
||||
}
|
||||
|
||||
case "${1:-list}" in
|
||||
sync) cmd_sync ;;
|
||||
list|ls|status) cmd_list ;;
|
||||
add) shift; cmd_add "${1:-}" ;;
|
||||
rm|remove|del) shift; cmd_rm "${1:-}" ;;
|
||||
edit) cmd_edit ;;
|
||||
log|logs) shift; cmd_log "${1:-}" ;;
|
||||
*) cat >&2 <<EOF
|
||||
usage: claude-rc <cmd>
|
||||
list projects + running state (default)
|
||||
add [path] add project (default: cwd)
|
||||
rm <path> remove project (comments it out)
|
||||
edit \$EDITOR the list, then sync
|
||||
sync force reconcile
|
||||
log <path> journal for one project
|
||||
list file: $LIST
|
||||
EOF
|
||||
exit 1 ;;
|
||||
esac
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Launch a Claude Code Remote Control server for one project dir.
|
||||
# Invoked by claude-rc@.service; arg is the project path.
|
||||
set -euo pipefail
|
||||
|
||||
dir="${1:?usage: claude-rc-run <project-dir>}"
|
||||
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo "claude-rc-run: no such directory: $dir" >&2
|
||||
exit 78 # EX_CONFIG - systemd won't spin on Restart=on-failure
|
||||
fi
|
||||
|
||||
# --spawn worktree needs a git repo; fall back to same-dir if not one
|
||||
spawn=worktree
|
||||
git -C "$dir" rev-parse --git-dir >/dev/null 2>&1 || {
|
||||
echo "claude-rc-run: $dir is not a git repo, using --spawn same-dir" >&2
|
||||
spawn=same-dir
|
||||
}
|
||||
|
||||
# Optional display name: a projects-file line may read
|
||||
# /path/to/repo | My Display Name
|
||||
# Falls back to the bare directory name.
|
||||
LIST="${CLAUDE_RC_LIST:-$HOME/.config/claude-rc/projects}"
|
||||
name=$(awk -F'|' -v t="$dir" '
|
||||
{ line=$0; sub(/#.*/,"",line)
|
||||
p=line; if (index(line,"|")) { p=substr(line,1,index(line,"|")-1) }
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",p); sub(/\/+$/,"",p)
|
||||
if (p == t && index(line,"|")) {
|
||||
n=substr(line,index(line,"|")+1)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",n)
|
||||
if (n != "") { print n; exit }
|
||||
}
|
||||
}' "$LIST" 2>/dev/null || true)
|
||||
[ -n "$name" ] || name="$(basename "$dir")"
|
||||
|
||||
cd "$dir"
|
||||
exec claude remote-control \
|
||||
--spawn "$spawn" \
|
||||
--permission-mode bypassPermissions \
|
||||
--name "$name"
|
||||
@@ -15,35 +15,9 @@ let
|
||||
toolPath = "%h/.local/bin:%h/.nix-profile/bin:/etc/profiles/per-user/naps62/bin:/run/current-system/sw/bin";
|
||||
|
||||
sem = inputs.sem.packages.${pkgs.system}.default;
|
||||
|
||||
# runtimeInputs is prepended to PATH, not a replacement, so `claude` still
|
||||
# resolves from the unit's own PATH.
|
||||
claude-rc-run = pkgs.writeShellApplication {
|
||||
name = "claude-rc-run";
|
||||
runtimeInputs = with pkgs; [
|
||||
git
|
||||
gawk
|
||||
coreutils
|
||||
];
|
||||
text = builtins.readFile ./bin/claude-rc-run;
|
||||
};
|
||||
|
||||
claude-rc = pkgs.writeShellApplication {
|
||||
name = "claude-rc";
|
||||
runtimeInputs = with pkgs; [
|
||||
git
|
||||
gawk
|
||||
gnused
|
||||
gnugrep
|
||||
coreutils
|
||||
systemd
|
||||
];
|
||||
text = builtins.readFile ./bin/claude-rc;
|
||||
};
|
||||
in
|
||||
{
|
||||
home.packages = [
|
||||
claude-rc
|
||||
sem
|
||||
pkgs.bun
|
||||
];
|
||||
@@ -90,15 +64,6 @@ in
|
||||
Install.WantedBy = [ "default.target" ];
|
||||
};
|
||||
|
||||
claude-rc-sync = {
|
||||
Unit.Description = "Reconcile Claude Remote Control servers with the project list";
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${claude-rc}/bin/claude-rc sync";
|
||||
Environment = [ "PATH=${toolPath}" ];
|
||||
};
|
||||
};
|
||||
|
||||
hourlog = {
|
||||
Unit = {
|
||||
Description = "Start the Friday hour log in a tmux session";
|
||||
@@ -154,49 +119,6 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
# A plain file, NOT systemd.user.services: home-manager tries to start every
|
||||
# unit it manages, and starting a template without an instance is an error
|
||||
# ("missing the instance name"). Instances are enabled by `claude-rc sync`,
|
||||
# which needs the [Install] section below to exist.
|
||||
xdg.configFile."systemd/user/claude-rc@.service".text = ''
|
||||
[Unit]
|
||||
Description=Claude Code Remote Control (/%I)
|
||||
Documentation=https://code.claude.com/docs/en/remote-control
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StopWhenUnneeded=no
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Leading "-": a missing dir must not be fatal, or systemd fails with
|
||||
# 200/CHDIR before claude-rc-run can report the friendlier exit 78.
|
||||
WorkingDirectory=-/%I
|
||||
Environment=PATH=${toolPath}
|
||||
ExecStart=${claude-rc-run}/bin/claude-rc-run /%I
|
||||
# `always`, not `on-failure`: a >10min outage times the session out and the
|
||||
# process exits 0, which on-failure would not restart.
|
||||
Restart=always
|
||||
RestartSec=15
|
||||
# 78 = dir gone; 200 = systemd CHDIR failure. Without these, a deleted
|
||||
# project dir restart-loops every 15s forever.
|
||||
RestartPreventExitStatus=78 200
|
||||
StandardOutput=append:%h/.local/state/claude-rc/%i.log
|
||||
StandardError=inherit
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
'';
|
||||
|
||||
# systemd will not create the parent of StandardOutput=append:, and fails the
|
||||
# unit with 209/STDOUT if it is missing.
|
||||
home.file.".local/state/claude-rc/.keep".text = "";
|
||||
|
||||
systemd.user.paths.claude-rc = {
|
||||
Unit.Description = "Watch the Claude Remote Control project list for edits";
|
||||
Path = {
|
||||
PathChanged = "%h/.config/claude-rc/projects";
|
||||
Unit = "claude-rc-sync.service";
|
||||
};
|
||||
Install.WantedBy = [ "default.target" ];
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user