From d43f670285cd1ca2ef4b50ed61be057e67e8b793 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Sun, 23 Aug 2026 05:55:04 +0200 Subject: [PATCH 1/2] CB-624: add scripts/rename-checkout.sh to rename the checkout safely Renames ~/LTMS/claude-bridge -> ~/LTMS/fleetd as one auditable command, modeled on redeploy-bridged.sh. --check reports every surface holding the old absolute path (checkout files, launchd plist, Claude Code project state, worktree .git pointers, running daemon). Apply mode stops the daemon first (launchctl-aware), moves the checkout and the Claude Code project-state slug dir derived from both paths, repairs worktree gitdir pointers, rewrites bridged.yaml and the installed plist if they exist, restarts, and verifies /healthz plus a fresh 'bridged listening' line anchored to a pre-stop marker. --- scripts/rename-checkout.sh | 374 +++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100755 scripts/rename-checkout.sh diff --git a/scripts/rename-checkout.sh b/scripts/rename-checkout.sh new file mode 100755 index 0000000..9243aed --- /dev/null +++ b/scripts/rename-checkout.sh @@ -0,0 +1,374 @@ +#!/usr/bin/env bash +# +# Rename this checkout from ~/LTMS/claude-bridge to ~/LTMS/fleetd. +# +# The project was renamed upstream (fleet/fleetd on git.ltms.dev), but the checkout keeps its old +# name. Renaming it by hand misses surfaces, because the absolute path is written down in more +# places than the directory itself: +# +# 1. The launchd agent (~/Library/LaunchAgents/dev.ltms.bridged.plist) hard-codes absolute paths +# to the wrapper script and the jar. Move the checkout without fixing it and the daemon never +# starts again after a reboot. +# 2. Every git worktree carries a `.git` FILE with an ABSOLUTE pointer into the main checkout +# (`gitdir: /…/claude-bridge/.git/worktrees/`). Rename the main checkout and every +# worktree breaks at once ("not a git repository"). +# 3. Claude Code keys saved sessions and memory by a slug derived from the working directory, +# with `/` and `.` replaced by `-`. Move the checkout and it computes a NEW slug, finds +# nothing, and silently starts empty — every saved session is still on disk, just unreachable. +# 4. bridged/bridged.yaml is gitignored, so it survives only in the live checkout and holds +# `fleet.leaders.*.cwd` and `configDir` — both absolute, both old. +# 5. A running daemon holds the old paths open. Moving underneath it corrupts its state, so the +# daemon stops FIRST, and the script waits for the process to actually exit rather than +# assuming (same trap as redeploy-bridged.sh). +# 6. An old daemon that never died looks identical from the outside, so verification polls +# /healthz AND demands a FRESH "bridged listening" log line dated after the restart, anchored +# to a line-count marker taken before anything moved. +# +# Usage: +# scripts/rename-checkout.sh --check # report what holds the old path; changes nothing +# scripts/rename-checkout.sh # stop daemon, move everything, restart, verify +# scripts/rename-checkout.sh --yes # skip the confirmation prompt +# +# Run this FROM INSIDE OR OUTSIDE the checkout — it operates on fixed absolute paths, never on +# $PWD, because after the rename $PWD itself is gone. +# +# Exits non-zero on any failure. A failed pre-flight never touches the daemon. + +set -euo pipefail + +OLD_PATH="/Users/dai.ha/LTMS/claude-bridge" +NEW_PATH="/Users/dai.ha/LTMS/fleetd" +BRIDGED_SUB="bridged" # daemon lives at /bridged/ +JAR_NAME="bridged.jar" +OUT_NAME="bridged.out" + +CC_INSTANCE="ltms" +CC_PROJECTS="$HOME/.ccs/instances/$CC_INSTANCE/projects" + +LAUNCHD_LABEL='dev.ltms.bridged' +LAUNCHD_PLIST="$HOME/Library/LaunchAgents/$LAUNCHD_LABEL.plist" + +HEALTH='http://127.0.0.1:8765/healthz' +PATTERN="target/$JAR_NAME" +STOP_WAIT=30 # seconds to wait for a clean daemon exit before reporting failure +HEALTH_WAIT=60 # seconds to wait for /healthz to answer after start + +ASSUME_YES=0; CHECK_ONLY=0 +for arg in "$@"; do + case "$arg" in + --check) CHECK_ONLY=1 ;; + --yes|-y) ASSUME_YES=1 ;; + -h|--help) sed -n '3,40p' "${BASH_SOURCE[0]}"; exit 0 ;; + *) echo "unknown option: $arg (try --help)" >&2; exit 2 ;; + esac +done + +say() { printf '\n\033[1m== %s\033[0m\n' "$*"; } +ok() { printf ' ok %s\n' "$*"; } +warn() { printf ' WARN %s\n' "$*"; } +die() { printf '\n FAIL %s\n\n' "$*" >&2; exit 1; } + +running_pid() { pgrep -f "$PATTERN" || true; } +launchd_installed() { [ -f "$LAUNCHD_PLIST" ]; } +launchd_loaded() { launchctl list "$LAUNCHD_LABEL" >/dev/null 2>&1; } + +# Claude Code derives its per-project directory name from the working directory by replacing +# "/" and "." with "-". Derived here, never hardcoded, so the script stays correct if either +# endpoint path ever changes again. +cc_slug() { printf '%s' "$1" | tr '/.' '-'; } + +OLD_OUT="$OLD_PATH/$BRIDGED_SUB/$OUT_NAME" +NEW_OUT="$NEW_PATH/$BRIDGED_SUB/$OUT_NAME" + +# Files under a directory that contain the literal old path, with a hit count each. Read-only. +# Skips .git internals, build output and node_modules: they are generated, huge, and repaired by +# rebuilding rather than rewriting. +scan_old_path_in() { + local root="$1" + [ -e "$root" ] || return 0 + grep -rIlF --exclude-dir=.git --exclude-dir=target --exclude-dir=node_modules \ + "$OLD_PATH" "$root" 2>/dev/null || true +} + +report_hits() { + local label="$1" root="$2" f n total=0 + while IFS= read -r f; do + n="$(grep -cF "$OLD_PATH" "$f" 2>/dev/null || true)" + printf ' %5d %s\n' "$n" "${f#"$root"/}" + total=$((total + 1)) + done < <(scan_old_path_in "$root") + if [ "$total" -eq 0 ]; then + ok "$label: no files reference the old path" + else + warn "$label: $total file(s) reference the old path:" + fi +} + +# ---------------------------------------------------------------------- --check + +if [ "$CHECK_ONLY" = 1 ]; then + say "--check (read-only): who holds $OLD_PATH" + + say "1. files containing the old absolute path" + if [ -d "$OLD_PATH" ]; then + report_hits "checkout ($OLD_PATH)" "$OLD_PATH" + else + warn "checkout $OLD_PATH does not exist — skipping its scan" + fi + if launchd_installed; then + report_hits "installed launchd plist ($LAUNCHD_PLIST)" "$LAUNCHD_PLIST" + else + warn "installed launchd plist not found at $LAUNCHD_PLIST" + fi + report_hits "Claude Code instance data ($CC_PROJECTS)" "$CC_PROJECTS" + + say "2. launchd agent" + if launchd_installed; then + ok "installed: $LAUNCHD_PLIST" + ARGS="$(/usr/libexec/PlistBuddy -c 'Print :ProgramArguments' "$LAUNCHD_PLIST" 2>/dev/null | grep -F '/' || true)" + if printf '%s' "$ARGS" | grep -qF "$OLD_PATH"; then + warn "plist ProgramArguments point at the OLD path:" + printf '%s\n' "$ARGS" | sed 's/^/ /' + elif [ -n "$ARGS" ]; then + ok "plist ProgramArguments do not reference the old path" + else + warn "could not read ProgramArguments from the plist" + fi + else + warn "launchd agent NOT installed (daemon would be started by hand only)" + fi + if launchd_loaded; then + ok "loaded: launchd supervises '$LAUNCHD_LABEL'" + else + warn "launchd agent not loaded" + fi + + say "3. Claude Code project-state directory" + OLD_SLUG="$(cc_slug "$OLD_PATH")"; NEW_SLUG="$(cc_slug "$NEW_PATH")" + if [ -d "$CC_PROJECTS/$OLD_SLUG" ]; then + ok "old slug EXISTS: $CC_PROJECTS/$OLD_SLUG ($(find "$CC_PROJECTS/$OLD_SLUG" -type f | wc -l | tr -d ' ') files)" + else + warn "old slug missing: $CC_PROJECTS/$OLD_SLUG" + fi + if [ -e "$CC_PROJECTS/$NEW_SLUG" ]; then + warn "new slug ALREADY EXISTS: $CC_PROJECTS/$NEW_SLUG — moving over it needs a decision" + else + ok "new slug absent (clean): $CC_PROJECTS/$NEW_SLUG" + fi + + say "4. git worktrees" + if [ -d "$OLD_PATH" ]; then + git -C "$OLD_PATH" worktree list || warn "'git worktree list' failed" + BROKEN=0 + while IFS= read -r wt; do + [ "$wt" = "$OLD_PATH" ] && continue # the main checkout moves WITH the repo + GITFILE="$wt/.git" + if [ -f "$GITFILE" ] && grep -qF "$OLD_PATH" "$GITFILE" 2>/dev/null; then + warn "worktree $wt — .git pointer references the old path (breaks on rename)" + BROKEN=$((BROKEN + 1)) + fi + done < <(git -C "$OLD_PATH" worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p') + if [ "$BROKEN" -eq 0 ]; then + ok "no external worktree .git pointer references the old path" + fi + else + warn "checkout $OLD_PATH missing — cannot enumerate worktrees" + fi + + say "5. daemon process" + PID="$(running_pid)" + if [ -n "$PID" ]; then + ok "daemon running, pid $PID" + CMD="$(ps -p "$PID" -o command= 2>/dev/null || true)" + if printf '%s' "$CMD" | grep -qF "$OLD_PATH"; then + warn "it runs the OLD path: $CMD" + else + ok "command: $CMD" + fi + else + warn "no daemon running" + fi + + say "--check done: nothing changed" + exit 0 +fi + +# ------------------------------------------------------------------- apply mode + +say "pre-flight" +[ -d "$OLD_PATH" ] || die "old checkout $OLD_PATH does not exist — nothing to rename" +[ -e "$NEW_PATH" ] && die "new path $NEW_PATH already exists — refusing to overwrite" +if [ -n "$(git -C "$OLD_PATH" status --porcelain 2>/dev/null)" ]; then + git -C "$OLD_PATH" status --short | sed 's/^/ /' + die "working tree at $OLD_PATH is dirty — commit or stash first, then rerun" +fi +ok "working tree clean, target path free" + +if [ "$ASSUME_YES" = 0 ]; then + echo + echo " This MOVES the checkout this system runs from and RESTARTS the daemon." + echo " If you are connected through that daemon, your connection dies mid-turn." + echo + read -r -p " Type yes to continue: " reply + [ "$reply" = "yes" ] || die "aborted — nothing changed" +fi + +SUPERVISED=0 +launchd_loaded && SUPERVISED=1 + +# ------------------------------------------------------- stop the daemon FIRST +say "stop" +OLD_PID="$(running_pid)" +RESTART_MARK="$(wc -l < "$OLD_OUT" 2>/dev/null || echo 0)" # demand a FRESH listening line later +if [ -n "$OLD_PID" ]; then + if [ "$SUPERVISED" = 1 ]; then + echo " supervision is ON: 'launchctl unload' (not kill) so launchd's KeepAlive cannot" + echo " restart the OLD jar out from under this script." + launchctl unload -w "$LAUNCHD_PLIST" \ + || die "launchctl unload failed — investigate before retrying; NOTHING has been moved" + else + kill "$OLD_PID" + fi + for _ in $(seq "$STOP_WAIT"); do + [ -z "$(running_pid)" ] && break + sleep 1 + done + [ -z "$(running_pid)" ] || die "pid $OLD_PID still alive after ${STOP_WAIT}s — NOT escalating to kill -9 and NOT moving anything. Wait or stop it by hand." + ok "pid $OLD_PID exited" +elif [ "$SUPERVISED" = 1 ]; then + launchctl unload -w "$LAUNCHD_PLIST" 2>/dev/null || true + ok "launchd agent unloaded (daemon was not running)" +else + ok "daemon was not running" +fi + +# ------------------------------------------------------------------- move things +say "move" +mv "$OLD_PATH" "$NEW_PATH" || die "mv $OLD_PATH -> $NEW_PATH failed — the checkout is now at its old name, nothing else was touched" +ok "checkout moved: $OLD_PATH -> $NEW_PATH" + +OLD_SLUG="$(cc_slug "$OLD_PATH")"; NEW_SLUG="$(cc_slug "$NEW_PATH")" +if [ -d "$CC_PROJECTS/$OLD_SLUG" ]; then + if [ -e "$CC_PROJECTS/$NEW_SLUG" ]; then + warn "NOT moving Claude Code state: $CC_PROJECTS/$NEW_SLUG already exists — merge by hand" + else + mv "$CC_PROJECTS/$OLD_SLUG" "$CC_PROJECTS/$NEW_SLUG" \ + || warn "moving Claude Code project state failed — sessions remain under the old slug" + ok "Claude Code project state moved: $OLD_SLUG -> $NEW_SLUG" + fi +else + warn "no Claude Code project state at $CC_PROJECTS/$OLD_SLUG — nothing to move" +fi + +# ------------------------------------------------------- repair the worktrees +say "repair worktrees" +FIXED=0 +for GITDIR_FILE in "$NEW_PATH"/.git/worktrees/*/gitdir; do + [ -f "$GITDIR_FILE" ] || continue + WT_GIT="$(cat "$GITDIR_FILE")" + if [ -f "$WT_GIT" ] && grep -qF "$OLD_PATH" "$WT_GIT"; then + sed -i '' "s|$OLD_PATH|$NEW_PATH|g" "$WT_GIT" \ + || warn "could not rewrite $WT_GIT — that worktree stays broken until fixed by hand" + FIXED=$((FIXED + 1)) + fi +done +if git -C "$NEW_PATH" worktree list >/dev/null 2>&1; then + ok "main repo answers after the rename" +else + die "git cannot read $NEW_PATH after the rename — inspect .git by hand" +fi +if [ "$FIXED" -gt 0 ]; then + ok "rewrote $FIXED worktree .git pointer(s) to the new path" +else + ok "no worktree pointer referenced the old path" +fi + +# ------------------------------------- rewrite gitignored config, if it survived +YAML="$NEW_PATH/$BRIDGED_SUB/bridged.yaml" +if [ -f "$YAML" ]; then + if grep -qF "$OLD_PATH" "$YAML"; then + cp "$YAML" "$YAML.pre-rename.bak" + sed -i '' "s|$OLD_PATH|$NEW_PATH|g" "$YAML" + ok "rewrote $YAML (backup: $YAML.pre-rename.bak)" + else + ok "$YAML does not reference the old path" + fi +else + warn "no $YAML present — skipped (this branch could not be exercised from a worktree)" +fi + +# --------------------------------------------------------- fix + reload launchd +say "launchd" +if launchd_installed; then + if grep -qF "$OLD_PATH" "$LAUNCHD_PLIST"; then + cp "$LAUNCHD_PLIST" "$LAUNCHD_PLIST.pre-rename.bak" + sed -i '' "s|$OLD_PATH|$NEW_PATH|g" "$LAUNCHD_PLIST" + plutil -lint "$LAUNCHD_PLIST" >/dev/null \ + || die "rewritten plist fails plutil -lint — restore $LAUNCHD_PLIST.pre-rename.bak and fix by hand" + ok "rewrote $LAUNCHD_PLIST (backup beside it)" + else + ok "installed plist does not reference the old path" + fi +else + warn "launchd agent not installed — starting unsupervised below" +fi + +# ------------------------------------------------------------- start + verify +say "start" +if [ "$SUPERVISED" = 1 ]; then + launchctl load -w "$LAUNCHD_PLIST" || die "launchctl load failed — recover with: launchctl load -w \"$LAUNCHD_PLIST\"" +else + JAR="$NEW_PATH/$BRIDGED_SUB/target/$JAR_NAME" + [ -f "$JAR" ] || die "no jar at $JAR — build in the new location, then rerun" + ( cd "$NEW_PATH/$BRIDGED_SUB" && zsh -lc "nohup java -jar '$JAR' >> $OUT_NAME 2>&1 &" ) +fi + +for _ in $(seq 10); do + NEW_PID="$(running_pid)"; [ -n "$NEW_PID" ] && break; sleep 1 +done +[ -n "${NEW_PID:-}" ] || die "no daemon process appeared. Last lines of $NEW_OUT: +$(tail -20 "$NEW_OUT" 2>/dev/null)" +[ "$NEW_PID" != "${OLD_PID:-}" ] || die "pid unchanged ($NEW_PID) — the old daemon never died" +ok "started, pid $NEW_PID" + +say "verify" +HEALTH_OK=0; CODE="" +for _ in $(seq "$HEALTH_WAIT"); do + CODE="$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 "$HEALTH" 2>/dev/null || echo 000)" + case "$CODE" in + 200) HEALTH_OK=1; ok "/healthz 200"; break ;; + # 503 still means the daemon is UP — it means herdr is unreachable. + 503) HEALTH_OK=1; warn "/healthz answers 503 degraded — daemon up, herdr unreachable"; break ;; + esac + sleep 1 +done +[ "$HEALTH_OK" = 1 ] || die "/healthz never answered within ${HEALTH_WAIT}s (last code: ${CODE:-000})" + +if tail -n "+$((RESTART_MARK + 1))" "$NEW_OUT" 2>/dev/null | grep -q 'bridged listening'; then + ok "$(tail -n "+$((RESTART_MARK + 1))" "$NEW_OUT" | grep 'bridged listening' | tail -1)" +else + warn "no fresh 'bridged listening' line after the restart — check $NEW_OUT yourself" +fi + +# ------------------------------------------------------------------- summary +say "changed" +echo " checkout: $OLD_PATH -> $NEW_PATH" +echo " claude code state: $CC_PROJECTS/$OLD_SLUG -> $CC_PROJECTS/$NEW_SLUG (when present)" +echo " worktree .git ptr: rewritten where they named the old path" +[ -f "$YAML" ] && echo " bridged.yaml: rewritten (backup kept)" +launchd_installed && echo " launchd plist: rewritten (backup kept)" + +say "still referencing the old path (left alone on purpose)" +LEFT=$(scan_old_path_in "$NEW_PATH") +if [ -n "$LEFT" ]; then + printf '%s\n' "$LEFT" | sed 's/^/ /' + echo " Review these by hand: tracked docs and IDE state are yours to update," + echo " session history must not be rewritten wholesale." +else + ok "(none inside the checkout)" +fi +scan_old_path_in "$CC_PROJECTS" | head -5 | sed 's/^/ /' || true +echo " (session transcripts quote the old cwd — history, left untouched)" + +echo +echo " Next: reconnect leads/members to the renamed checkout and re-check with --check." -- 2.52.0 From 51bdec22e74e9120de1be4bcef0eac3f97e4efa2 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Sun, 23 Aug 2026 06:07:16 +0200 Subject: [PATCH 2/2] CB-624: report three more rename surfaces as report-only --check gains section 6 counting ~/.claude.json (the projects entry keyed by the old absolute path), ~/.config/herdr/session.json, and JetBrains recentProjects.xml / trusted-paths.xml across every IntelliJIdea* version. Apply mode's closing summary lists the same three with manual follow-ups. None of the three is rewritten automatically, on purpose: ~/.claude.json is global live Claude Code config, herdr session.json is live process state, and JetBrains rewrites its own files when the project is reopened at the new path. Header comment states the reason for each so it does not read as an oversight. JetBrains stores these paths as its $USER_HOME$ macro rather than a literal absolute path, so the counter matches both forms and says which form the hits used. --- scripts/rename-checkout.sh | 110 +++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/scripts/rename-checkout.sh b/scripts/rename-checkout.sh index 9243aed..69dbbc7 100755 --- a/scripts/rename-checkout.sh +++ b/scripts/rename-checkout.sh @@ -24,6 +24,22 @@ # /healthz AND demands a FRESH "bridged listening" log line dated after the restart, anchored # to a line-count marker taken before anything moved. # +# Three more surfaces hold the old path but this script REPORTS them and never rewrites them. +# That is a decision, not an oversight: +# +# A. ~/.claude.json — Claude Code's GLOBAL config for every project on this machine (the +# `projects` map keys projects by their absolute path). It is written by live sessions while +# this script could run, and one bad edit there breaks every project on the machine, not just +# this repo. The entry holds trust (`hasTrustDialogAccepted`), onboarding state, +# `enabledMcpjsonServers`, `allowedTools` and `lastSessionId`. +# B. ~/.config/herdr/session.json — live session records of a RUNNING process. Rewriting it +# underneath herdr corrupts its state. +# C. JetBrains options (recentProjects.xml, trusted-paths.xml under any IntellIJIDEA* version) — +# the IDE rewrites these itself when the project is reopened at the new path, so editing them +# by hand is both unnecessary and racy with a possibly-running IDE. +# +# For each of the three, --check prints the exact manual follow-up instead of fixing it. +# # Usage: # scripts/rename-checkout.sh --check # report what holds the old path; changes nothing # scripts/rename-checkout.sh # stop daemon, move everything, restart, verify @@ -45,6 +61,12 @@ OUT_NAME="bridged.out" CC_INSTANCE="ltms" CC_PROJECTS="$HOME/.ccs/instances/$CC_INSTANCE/projects" +# Report-only surfaces (see header, items A-C) — never rewritten by apply mode. +CLAUDE_JSON="$HOME/.claude.json" +HERDR_SESSION="$HOME/.config/herdr/session.json" +JETBRAINS_ROOT="$HOME/Library/Application Support/JetBrains" +JETBRAINS_FILES=(recentProjects.xml trusted-paths.xml) + LAUNCHD_LABEL='dev.ltms.bridged' LAUNCHD_PLIST="$HOME/Library/LaunchAgents/$LAUNCHD_LABEL.plist" @@ -104,6 +126,88 @@ report_hits() { fi } +# --------------------------------------------------------------------------- +# Report-only surfaces (header items A-C). Counted here, NEVER rewritten by +# apply mode — the header comment explains why each is left alone. Both +# --check (section 6) and apply mode's closing summary call this, so the two +# reports can never drift apart. +# --------------------------------------------------------------------------- + +count_old_path_in_file() { # -> stdout: hit count (0 if file absent) + if [ ! -f "$1" ]; then echo 0; return; fi + # "no match" makes grep exit 1 and, under `set -o pipefail`, the whole pipeline too — so the + # assignment needs its own `|| true`; wc still prints the correct 0. + local n + n="$(grep -oF -- "$2" "$1" 2>/dev/null | wc -l | tr -d ' ')" || true + printf '%s' "${n:-0}" +} + +report_only_surfaces() { + local n + + # A. Claude Code global config — the per-project entry keyed by absolute path. + if [ -f "$CLAUDE_JSON" ]; then + n="$(count_old_path_in_file "$CLAUDE_JSON" "$OLD_PATH")" + KEY="unknown" + if command -v python3 >/dev/null 2>&1; then + KEY="$(python3 -c 'import json,sys +d = json.load(open(sys.argv[1])) +print("present" if sys.argv[2] in d.get("projects", {}) else "absent")' \ + "$CLAUDE_JSON" "$OLD_PATH" 2>/dev/null)" || KEY="unknown" + fi + warn "A. $CLAUDE_JSON — old-path occurrences: $n; projects[\"$OLD_PATH\"] entry: $KEY" + echo " holds trust/onboarding state, enabledMcpjsonServers, allowedTools, lastSessionId." + echo " FOLLOW-UP: quit every Claude Code session first (this file is rewritten on exit)," + echo " then rename the key projects[\"$OLD_PATH\"] -> projects[\"$NEW_PATH\"], keeping the" + echo " entry's contents. If skipped: the trust dialog appears once at the new path and" + echo " .mcp.json servers start disabled until re-enabled." + else + ok "A. $CLAUDE_JSON absent — nothing to report" + fi + + # B. herdr live session records. + n="$(count_old_path_in_file "$HERDR_SESSION" "$OLD_PATH")" + if [ "$n" -gt 0 ]; then + warn "B. $HERDR_SESSION — $n occurrence(s) of the old path (live herdr state)" + echo " FOLLOW-UP: stop the daemon/herdr FIRST, then edit the file (or leave it: sessions" + echo " started from the new checkout afterwards use the new path). Never edit while herdr runs." + elif [ -f "$HERDR_SESSION" ]; then + ok "B. $HERDR_SESSION does not reference the old path" + else + ok "B. $HERDR_SESSION absent" + fi + + # C. JetBrains recent projects + trusted paths, any IntelliJIdea version. + # JetBrains stores these paths as its own "$USER_HOME$/..." MACRO, not as the literal absolute + # path, so a plain absolute-path grep reports 0 hits on files that are in fact full of them. + # Count both forms; report the macro form when that is where the hits live. + FOUND=0 + JET_MACRO='$USER_HOME$'"${OLD_PATH#"$HOME"}" # e.g. $USER_HOME$/LTMS/claude-bridge + if [ -d "$JETBRAINS_ROOT" ]; then + for VDIR in "$JETBRAINS_ROOT"/IntelliJIdea*/; do + [ -d "$VDIR" ] || continue + VDIR="${VDIR%/}" + for F in "${JETBRAINS_FILES[@]}"; do + FPATH="$VDIR/options/$F" + ABS_N="$(count_old_path_in_file "$FPATH" "$OLD_PATH")" + MACRO_N="$(count_old_path_in_file "$FPATH" "$JET_MACRO")" + n=$((ABS_N + MACRO_N)) + if [ "$n" -gt 0 ]; then + warn "C. ${FPATH#"$HOME"/} — $n occurrence(s) of the old path ($MACRO_N as the \${USER_HOME} macro, $ABS_N literal)" + FOUND=$((FOUND + 1)) + fi + done + done + fi + if [ "$FOUND" -eq 0 ]; then + ok "C. no JetBrains IntelliJIdea* options file references the old path" + else + echo " FOLLOW-UP: open the project at $NEW_PATH in IntelliJ — the IDE rewrites" + echo " recentProjects.xml and trusted-paths.xml itself (it asks to trust once). Editing" + echo " these by hand is unnecessary and racy with a running IDE." + fi +} + # ---------------------------------------------------------------------- --check if [ "$CHECK_ONLY" = 1 ]; then @@ -189,6 +293,9 @@ if [ "$CHECK_ONLY" = 1 ]; then warn "no daemon running" fi + say "6. report-only surfaces (counted, NEVER rewritten — see header items A-C)" + report_only_surfaces + say "--check done: nothing changed" exit 0 fi @@ -370,5 +477,8 @@ fi scan_old_path_in "$CC_PROJECTS" | head -5 | sed 's/^/ /' || true echo " (session transcripts quote the old cwd — history, left untouched)" +say "left alone on purpose — report-only surfaces and their manual follow-up" +report_only_surfaces + echo echo " Next: reconnect leads/members to the renamed checkout and re-check with --check." -- 2.52.0