Skip to content

Follow-up: remote-host and hook-cleanup gaps around $CLAUDE_CONFIG_DIR #271

Description

@halindrome

Split out of #270 so that PR stays reviewable. #270 fixes local session discovery for users with a custom $CLAUDE_CONFIG_DIR; this issue tracks the parts that were deliberately deferred because they cannot be verified without a remote host and were generating more defects than they resolved.

Filing this openly: I attempted all three of these inside #270 and three QA rounds found real bugs in each attempt, including a regression against main. They deserve to be designed and reviewed on their own, by someone who can test against a real remote host.

1. RemoteInstaller hardcodes ~/.claude

install_claude() in the embedded Python does claude_root = home / ".claude", so a remote host with a custom CLAUDE_CONFIG_DIR gets hooks where its Claude Code never reads them — the same silent failure #269 describes, on the remote path.

Two traps found while attempting this:

  • The guard is subtle. if not claude_root.exists() and shutil.which("claude") is None: return "Claude skipped". A custom-config user typically has no ~/.claude but does have claude on PATH, so the installer proceeds and creates a stale directory. My attempted fix added an early return that made the shutil.which check unreachable, turning "installs into ~/.claude" into "installs nothing" for hosts with Claude Code present but not yet run — worse than the original. Any fix needs to handle both directions.
  • A non-interactive SSH session does not source shell rc files, so $CLAUDE_CONFIG_DIR is usually absent exactly when it matters. configureRemoteHooks already runs the script via "${SHELL:-/bin/bash}" -lc for this reason (that is how $CODEX_HOME reaches it today), so the variable may be reachable — but this needs verifying on a real host, not assuming.

2. The remote hook script reads from ~/.claude/projects

Sources/CodeIsland/Resources/codeisland-remote-hook.py builds its transcript path from ~/.claude/projects directly. Fixing the installer alone delivers no end-to-end benefit: the hook fires but resolves no transcript, and transcript-derived enrichment silently degrades. Both sides have to move together.

Note also that NFC normalization is wrong here. macOS/APFS normalizes, but ext4 and xfs are byte-preserving — normalizing a Linux remote path can produce a directory that does not exist. The macOS-side resolver normalizes deliberately (matching Claude Code); a remote implementation should not copy that behavior.

3. Hook entries orphaned when the resolved config dir changes

If a user installs hooks, then changes their config dir (sets the preference, exports the variable, or populates a different directory), entries remain at the old location. They keep firing, and uninstall — which resolves to the new location — reports success while leaving them in place.

This is pre-existing behavior for every CLI in ConfigInstaller and not a regression from #270, but the custom-dir case makes it easier to hit. Attempted and reverted, because:

  • Sweeping a hardcoded [~/.claude, ~/.config/claude-code] misses any dir reached via the preference or the variable — i.e. exactly the case worth fixing.
  • Persisting the last-resolved dir covers install→change→uninstall, but not a user who installed under a previous app version, and the key is never cleared on uninstall.
  • The sweep rewrites files outside the resolved dir, so it needs a much stronger guard than a whole-file contains("codeisland") substring test, and real coverage. Deleting the exclusion filter that stops it from stripping the hooks it just wrote left my test suite green.

A durable fix probably wants an explicit record of installed hook locations rather than inference from the current resolution.


Happy to pick any of these up separately once #270 settles, though (1) and (2) really want someone with a remote host to validate against.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions