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.
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.
RemoteInstallerhardcodes~/.claudeinstall_claude()in the embedded Python doesclaude_root = home / ".claude", so a remote host with a customCLAUDE_CONFIG_DIRgets hooks where its Claude Code never reads them — the same silent failure #269 describes, on the remote path.Two traps found while attempting this:
if not claude_root.exists() and shutil.which("claude") is None: return "Claude skipped". A custom-config user typically has no~/.claudebut does haveclaudeon PATH, so the installer proceeds and creates a stale directory. My attempted fix added an early return that made theshutil.whichcheck 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.$CLAUDE_CONFIG_DIRis usually absent exactly when it matters.configureRemoteHooksalready runs the script via"${SHELL:-/bin/bash}" -lcfor this reason (that is how$CODEX_HOMEreaches 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/projectsSources/CodeIsland/Resources/codeisland-remote-hook.pybuilds its transcript path from~/.claude/projectsdirectly. 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
ConfigInstallerand not a regression from #270, but the custom-dir case makes it easier to hit. Attempted and reverted, because:[~/.claude, ~/.config/claude-code]misses any dir reached via the preference or the variable — i.e. exactly the case worth fixing.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.