Skip to content

fix: honor $CLAUDE_CONFIG_DIR instead of hardcoding ~/.claude#270

Open
halindrome wants to merge 5 commits into
wxtsky:mainfrom
halindrome:fix/honor-claude-config-dir
Open

fix: honor $CLAUDE_CONFIG_DIR instead of hardcoding ~/.claude#270
halindrome wants to merge 5 commits into
wxtsky:mainfrom
halindrome:fix/honor-claude-config-dir

Conversation

@halindrome

@halindrome halindrome commented Jul 19, 2026

Copy link
Copy Markdown

Fixes #269.

Problem

Claude Code locates projects/ and settings.json via $CLAUDE_CONFIG_DIR, falling back to ~/.claude only when unset. CodeIsland hardcoded ~/.claude at every call site, so users with a custom config dir got zero session detection — and with hideWhenNoSession on, the app hid itself completely, which presents as a crash.

Approach

Adds ClaudeConfigPaths to CodeIslandCore and routes the Claude paths through it:

  1. claude_config_dir preference (new Settings field)
  2. $CLAUDE_CONFIG_DIR
  3. ~/.claude, when populated
  4. ~/.config/claude-code, when populated
  5. ~/.claude

Three decisions worth calling out:

Why a preference and not just the env var. CodeIsland is normally launched from Finder or as a login item, and that process inherits no shell environment — the env var is absent exactly when it matters. An env-var-only version still finds nothing on a Finder launch; I verified this. The preference is the only channel that reliably survives.

Why a live ~/.claude outranks the XDG probe. Claude Code has no XDG rung at all. Probing ~/.config/claude-code first meant a stale XDG directory could silently repoint CodeIsland away from the directory Claude Code actually reads — reintroducing this issue's symptom through a different door.

Why liveness is a projects/ directory and not settings.json. CodeIsland's own hook installer creates settings.json. Counting it would let the app manufacture the signal it then reads back: an otherwise-empty ~/.claude holding only a CodeIsland-written settings.json would outrank the user's real config dir permanently. It must also be a directoryfileExists is true for a stray regular file of that name.

The hook installer reuses the existing rootOverride / displayPathOverride mechanism already used for $CODEX_HOME.

Scope

This PR deliberately covers local session discovery and hook placement only.

Earlier revisions also changed RemoteInstaller, the remote hook script, and added a cross-directory stale-hook cleanup sweep. Three QA rounds found repeated defects in those areas — including a regression against main — while the resolver itself stayed clean after round 1. They are reverted here and tracked in #271, with the specific traps each attempt hit written up, because they cannot be meaningfully verified without a remote host.

Known limitation: a user who changes their resolved config dir after installing hooks will have entries left at the old location. This is the pre-existing behavior for every CLI in ConfigInstaller and not a regression from this PR; see #271.

Compatibility

No behavior change for users without a custom config dir: a populated ~/.claude still wins, and with nothing populated anywhere resolution returns ~/.claude as before. Both covered by tests.

Testing

  • 560 tests, 0 failures (17 new).
  • Resolver tests cover each rung, tilde expansion, trailing slashes, NFC normalization, : in APFS filenames, non-absolute rejection, empty-XDG, both-populated precedence, settings.json-is-not-liveness, and file-vs-directory — the last against a real temp tree, since asserting it through the injected probe would prove nothing.
  • Wiring tests pin the preference-key parity across modules and the Claude rootOverride, without which fullPath silently becomes ~/settings.json.
  • Verified end-to-end on real hardware: with ~/.claude absent, no preference set, and the app launched via LaunchServices (so $CLAUDE_CONFIG_DIR is not inherited), sessions are discovered purely through the directory probe.
  • ja / ko / tr / zh / zh-Hant strings added.

Three rounds of QA are recorded in the comments below, including the findings that were mine.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YL1NKtwrKxPSxkBEuKGWNW

shanemccarron-maker and others added 2 commits July 19, 2026 08:57
Claude Code stores its transcripts and settings under $CLAUDE_CONFIG_DIR
when that variable is set, falling back to ~/.claude. CodeIsland only ever
looked at ~/.claude, so users with a custom config dir saw no Claude
sessions at all — and with hideWhenNoSession enabled the app hid itself
entirely, which reads as a crash.

Add ClaudeConfigPaths, a small resolver in CodeIslandCore, and route every
Claude path through it. The chain is:

  1. claude_config_dir preference (new Settings field)
  2. $CLAUDE_CONFIG_DIR
  3. ~/.config/claude-code, when it actually holds Claude state
  4. ~/.claude

The preference exists because CodeIsland is normally launched from Finder
or as a login item, where no shell environment is inherited — reading the
env var alone would not have fixed the reported case. The XDG probe checks
for projects/ or settings.json so an empty directory left behind by another
tool cannot shadow a real ~/.claude.

Behavior is unchanged for users without a custom config dir: with no
preference, no env var, and no populated ~/.config/claude-code, resolution
still returns ~/.claude.

Covered by 9 new tests against a pure, injectable resolve() core.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YL1NKtwrKxPSxkBEuKGWNW
Review caught two defects in the previous commit.

The colon-splitting was based on a claim I had not verified. Claude Code
reads CLAUDE_CONFIG_DIR verbatim as a single path — there is no PATH-style
list handling anywhere in it. Splitting on ":" was therefore not just
unnecessary but actively harmful: ":" is legal in an APFS filename, so a
directory like ~/Projects/AI:ML/claude was silently truncated to
~/Projects/AI.

Claude Code also applies .normalize("NFC") to the resolved path. Match that,
so a decomposed path — which some macOS APIs hand back — resolves to the
same directory rather than missing it.

Also drop defaultConfigDir, which was declared and never referenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YL1NKtwrKxPSxkBEuKGWNW
@halindrome

Copy link
Copy Markdown
Author

Self-review (QA pass)

Ran a review pass over this diff before asking for maintainer time. Two defects found and fixed in b0d1e5a; remaining observations noted below for your judgement.

Fixed

Colon-splitting was wrong and harmful. The first version split $CLAUDE_CONFIG_DIR on : and took the first entry, on the belief that it was a PATH-style list. I checked that against the actual Claude Code binary and it is false — the variable is read verbatim as a single path:

function Tsl(){ return process.env.CLAUDE_CONFIG_DIR }
aQe.join(process.env.CLAUDE_CONFIG_DIR || Lfl.homedir(), e)

Since : is legal in an APFS filename, the split silently truncated valid paths (~/Projects/AI:ML/claude~/Projects/AI). Removed, with a regression test.

Missing NFC normalization. Claude Code applies .normalize("NFC") to the resolved path; this did not. A decomposed path — which some macOS APIs return — would miss the directory. Now matched, with a test.

Also removed defaultConfigDir, which was declared and never referenced.

Remaining observations (not addressed — your call)

  • Resolution is uncached. configDir() re-reads UserDefaults, the environment, and up to two fileExists probes on each call. It is hoisted out of the loop in findActiveClaudeSessions, but SessionTitleStore and readModelFromTranscript call it per session per refresh tick. The syscalls are cheap and the Settings field already says a restart is needed, so a lazy cache would be consistent with documented behavior — I left it out to keep the diff minimal, but happy to add it.

  • No validation on the preference. A typo'd path yields zero sessions and no explanation, which is the exact failure mode this PR exists to fix. The "Currently using:" line does update live as you type, but a warning when the resolved directory holds no projects/ would close the loop properly.

  • hideWhenNoSession turns any detection failure into an apparent crash. Out of scope here, but this is what made the original bug so hard to diagnose — the app vanishes with no indication it is running. A "no sessions detected" affordance might be worth considering separately.

  • Stale hook config. Users with a custom config dir who previously installed hooks will have an inert ~/.claude/settings.json left behind. New installs write to the right place; the orphan is not cleaned up. Mentioned in the PR description — say the word and I will add migration.

Verification

  • 554 tests, 0 failures (10 new).
  • Rebuilt and re-ran end-to-end against a live machine: 3 concurrent Claude sessions discovered where the pre-fix build found none.

Resolves findings from the round-1 QA review.

F1 (major) — the XDG probe could shadow a live ~/.claude. Claude Code has no
XDG rung: it reads $CLAUDE_CONFIG_DIR, else ~/.claude. Probing ~/.config/
claude-code before checking whether ~/.claude was populated meant a stale XDG
directory silently repointed us away from the directory Claude Code actually
reads — reintroducing issue wxtsky#269's symptom through a different door. A live
~/.claude now wins.

Liveness is judged by projects/ alone, NOT settings.json: CodeIsland's own
hook installer creates settings.json, so counting it would let us manufacture
the signal we then read back — an otherwise-empty ~/.claude holding only a
CodeIsland-written settings.json would outrank the user's real config dir
permanently.

F2 (major) — install and uninstall could resolve to different directories, so
hook entries left at the old location kept firing and became unreachable by
uninstall, which still reported success. Both paths now sweep the
non-resolved candidate dirs, touching only files carrying our marker.

F3 (major) — preference/env values were accepted verbatim, so a relative path
or a typo was resolved against the app's cwd and the hook installer then
created the bogus directory and reported success. Non-absolute values (and
bare "/") now fall through to auto-detect instead.

F4 (major) — RemoteInstaller's embedded Python still hardcoded ~/.claude, and
its guard let a custom-config user through (no ~/.claude, but claude on PATH),
creating a stale dir on the remote host. It now mirrors the resolver.

F9 (minor) — configDir() re-resolved on every call, including stat() syscalls
on the main thread from SettingsView.body. Memoized on its inputs, which keeps
the live Settings preview correct while removing steady-state syscalls.

F5/F6/F8 (minor) — zh + zh-Hant strings added; tests pin the preference-key
parity across modules and the Claude rootOverride (without which fullPath
silently becomes ~/settings.json).

Also fixes a test-isolation bug this surfaced: RemoteInstallerHookMergeTests
sandboxes HOME but leaked $CLAUDE_CONFIG_DIR, so once the remote script began
honoring that variable the suite wrote hooks into the developer's real config
dir. Stripped, matching the existing CODEX_HOME precedent.

F10 (hypothetical) left as-is; documented in the PR thread.

559 tests, 0 failures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YL1NKtwrKxPSxkBEuKGWNW
@halindrome

Copy link
Copy Markdown
Author

QA Round 1

Reviewed by a 3-lens Claude panel (contract/security, regression/edges, schema/tests) synthesized into one finding set. 11 findings: 4 major, 7 minor. All 4 majors fixed in 789af1e.

Contract Verification

Contract source: issue #269. 9 satisfied, 5 partially-satisfied, 1 untested, 0 not-satisfied. The partials drove the major findings below and are now closed.

# Finding Severity Status
F1 XDG probe could shadow a live ~/.claude major / regression fixed
F2 Uninstall left hooks orphaned at the previous config dir major / regression fixed
F3 Preference/env accepted without absoluteness validation major / regression fixed
F4 RemoteInstaller embedded Python still hardcoded ~/.claude major / contract fixed
F5 zh / zh-Hant missing the new Settings strings minor fixed
F6 Claude rootOverride untested minor fixed
F7 No test for both dirs populated minor fixed
F8 preferenceKey duplicated across modules, no parity test minor fixed
F9 configDir() re-resolved per call, stat() on main thread minor fixed
F10 NFC applied inconsistently minor / hypothetical not fixed — see below
F11 PR description stale vs. code minor / observation fixed

The notable one (F1)

The first version probed ~/.config/claude-code before checking whether ~/.claude was populated. Claude Code has no XDG rung — it reads $CLAUDE_CONFIG_DIR, else ~/.claude — so a stale XDG directory could silently repoint CodeIsland away from the directory Claude Code actually reads, reintroducing this issue's exact symptom through a different door. A live ~/.claude now wins.

The subtlety in the fix: liveness is judged by projects/ alone, not settings.json. CodeIsland's own hook installer creates settings.json, so counting it would let the app manufacture the signal it then reads back — an otherwise-empty ~/.claude holding only a CodeIsland-written settings.json would outrank the user's real config dir permanently.

F10 — not fixed, deliberately

NFC is applied to preference/env values but the ~/.claude and XDG literals are built unnormalized. The reviewer marked this hypothetical with no reproduction, and the concern (byte-exact paths on some network mounts) cuts both ways — normalizing more aggressively could itself break a mount that wants the decomposed form. Recorded here rather than changed on speculation. Happy to revisit with a reproduction.

Test isolation bug surfaced during the round

Worth flagging separately, since it bit this machine: once the remote installer began honoring $CLAUDE_CONFIG_DIR, RemoteInstallerHookMergeTests — which sandboxes HOME but did not strip CLAUDE_CONFIG_DIR — wrote 8 hook entries into the developer's real config dir on swift test. Fixed by stripping the variable, matching the existing CODEX_HOME precedent two lines above. Anyone running this suite with a custom CLAUDE_CONFIG_DIR before 789af1e should check their settings.json for stray codeisland-remote-hook.py entries.


SAST review skipped

Code scanning is not available for wxtsky/CodeIsland (no enabled CodeQL/SARIF analysis). No SAST delta computed — graceful skip, non-blocking.

Dependabot (SCA) — advisory, repo-wide

Dependabot alerts unavailable (not enabled, or token lacks security_events scope). Skipped — non-blocking.


Schema change: none detected (macOS Swift app, no DDL or migration files in the diff).

Test suite: 559 tests, 0 failures (16 new). Rebuilt and re-verified end-to-end after the fixes.


QA performed by Claude Code (claude-opus-4-8)

Round 2 reviewed round 1's fixes and found them incomplete in several places.
Two corrections to the round-1 commit message first, since it overstated what
that commit did:

  - It said the RemoteInstaller guard "let a custom-config user through ...
    It now mirrors the resolver." Only `claude_root` changed; the guard line
    was untouched, and `git diff` shows it as context. The guard bug was
    described as fixed when it was not.
  - "Mirrors the resolver" was also wrong for the resolver itself: the Python
    applied no absoluteness check, no trailing-slash strip, and no NFC
    normalization, so it accepted exactly the relative values round-1 F3 was
    raised to reject.

Both are now actually fixed. The guard no longer treats `claude` on PATH as
licence to CREATE a config dir when resolution merely fell through to the
~/.claude default — that planted a stale directory on the remote host which
the local sweep cannot reach.

R2-F3 (major) — the stale-hook sweep only knew two hardcoded home candidates,
so a dir previously resolved via the preference or $CLAUDE_CONFIG_DIR was
never swept and its entries fired forever against a deleted hook script. The
resolved dir is now persisted at install time and included in the candidate
set, which covers custom-to-custom and custom-to-default transitions.

R2-F9 (minor) — that filter compared an NFC-normalized configDir() against
hand-built literals; a decomposed home path could let the *resolved* dir slip
through and be swept, deleting the hooks just installed. Both sides are now
canonicalized.

R2-F5/F6 (major) — all six localized descriptions and the type's own header
doc still stated the pre-F1 resolution order, so every locale told the user
something the code stopped doing.

R2-F4 (major) — the sweep rewrites files on disk outside the resolved dir and
had zero tests. Split the per-directory logic out and specced it: the marker
guard leaves an unrelated settings.json byte-identical, our entries are
stripped while user entries survive, a missing file is a no-op.

R2-F8 (minor) — testUnicodePathsAreNormalizedToNFC was vacuous. Swift compares
Strings by canonical equivalence, so it passed with the normalization deleted.
It now compares UTF-8 bytes; verified against a stripped implementation.

R2-F7 (minor) — invalidateCache() had no caller; wired into install().

R2-F10 (observation) — the remote hook read transcripts from ~/.claude/projects,
so fixing the installer delivered no end-to-end benefit. It now resolves the
same way.

564 tests, 0 failures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YL1NKtwrKxPSxkBEuKGWNW
@halindrome

Copy link
Copy Markdown
Author

QA Round 2

Fresh 3-lens panel against the round-1 fixes. 10 findings: 7 major, 3 minor. All fixed in 01b573c.

Round 2 was a harder review than round 1, because it reviewed round 1's own work — and found it incomplete in several places.

Two corrections to the round-1 commit message

Before the findings, two things that commit claimed and should not have:

  1. It said the RemoteInstaller guard "let a custom-config user through ... It now mirrors the resolver." Only claude_root changed. The guard line was untouched — git diff shows it as unmodified context. A fix was described as done when it was not.
  2. "Mirrors the resolver" was also wrong about the resolver itself: the Python applied no absoluteness check, no trailing-slash strip, and no NFC normalization, so it accepted exactly the relative values round-1 F3 existed to reject.

Both are now genuinely fixed. Flagging them explicitly because an inaccurate commit message is worse than no message — it stops the next reader from looking.

Findings

# Finding Severity Status
R2-F1 Remote Python resolver skipped the round-1 F3 validation major fixed
R2-F2 install_claude guard unchanged — F4's claimed repair absent major fixed
R2-F3 Stale-hook sweep never covered a previously-resolved custom dir major fixed
R2-F4 Sweep rewrites user files and had zero test coverage major fixed
R2-F5 All six locales stated the pre-F1 auto-detect order major fixed
R2-F6 Header doc documented the pre-F1 order and a deleted symbol major fixed
R2-F7 invalidateCache() shipped public with no caller minor fixed
R2-F8 NFC test was vacuous minor fixed
R2-F9 Stale-dir filter compared normalized vs. non-normalized paths minor fixed
R2-F10 Remote hook read transcripts from ~/.claude/projects major / observation fixed

The one that mattered most (R2-F3)

Round 1 added the sweep to keep install and uninstall symmetric, but staleClaudeConfigDirs() returned a hardcoded [~/.claude, ~/.config/claude-code]. A directory reached via the preference or $CLAUDE_CONFIG_DIR is in neither list, and no prior resolution was persisted — so:

  • install with claude_config_dir = /custom/a, then clear the preference and uninstall → the shared hook script is deleted, but /custom/a/settings.json keeps live entries invoking a now-missing script, on every Claude Code event, permanently unreachable by any later uninstall;
  • /custom/a/custom/b leaves the old entries firing alongside the new — the exact double-firing the sweep was added to prevent.

The symmetry claim held only for transitions whose old value was one of the two hardcoded candidates — i.e. not the custom-dir case this PR exists for. The resolved dir is now persisted at install time and included in the sweep.

R2-F8 — a test that proved nothing

testUnicodePathsAreNormalizedToNFC used XCTAssertEqual on two Strings. Swift compares Strings by canonical equivalence, so the decomposed and precomposed forms are already == and the assertion passed with the normalization deleted entirely. It now compares UTF-8 bytes, verified against a deliberately stripped implementation:

with normalization deleted, byte-assert passes: false   (want false)
old String== assert would pass: true                    (why it was vacuous)

R2-F10 — why the remote fix was inert

Round 1 fixed where the remote installer writes hooks, but the installed hook still read transcripts from ~/.claude/projects. On a host with a custom config dir the hook fired and resolved nothing, so the installer fix delivered no end-to-end benefit. Both sides now resolve identically.


SAST review skipped

Code scanning is not available for wxtsky/CodeIsland. No SAST delta computed — graceful skip, non-blocking.

Dependabot (SCA) — advisory, repo-wide

Dependabot alerts unavailable. Skipped — non-blocking.


Schema change: none detected.

Test suite: 564 tests, 0 failures (21 new across both rounds). Rebuilt, reinstalled, and re-verified end-to-end after the fixes; the real settings.json was checked for contamination and is clean.

Round 2 fixed 7 majors, so a round 3 is warranted before this is considered settled.


QA performed by Claude Code (claude-opus-4-8)

Three QA rounds found 33 findings. The finding rate did not decline (4, 7, 4
majors), but their location was consistent: the resolver and its call sites
stabilized after round 1, while every major since came from scope added while
responding to review comments — the remote installer and the stale-hook sweep.
Those two areas produced 10 of the 12 majors in rounds 2 and 3.

Round 3 made the case plainly. The round-2 "fix" to the remote installer guard
placed an early return ahead of the pre-existing shutil.which check, making it
unreachable and silently turning "installs hooks into ~/.claude" into "installs
nothing" for any host with Claude Code present but not yet run. That is a
regression against main, in code this PR does not need, that cannot be verified
without a remote host.

So this commit removes that scope rather than iterating on it again:

  - RemoteInstaller.swift, codeisland-remote-hook.py and
    RemoteInstallerHookMergeTests revert to origin/main.
  - The cross-directory stale-hook sweep and its last-resolved-dir persistence
    are removed, along with ClaudeStaleHookSweepTests.

What remains is the fix issue wxtsky#269 actually asks for: the resolver, the six
local call sites, the hook-install location, the Settings field, and their
tests. It is verified end-to-end on a real machine and has produced no majors
since round 1.

Known limitation, documented on the PR and deferred to a follow-up issue: a
user who changes their resolved config dir after installing hooks will have
entries left behind at the old location. That is the pre-existing behavior for
every other CLI in ConfigInstaller, it is not a regression, and the sweep that
attempted to fix it was itself the source of five majors.

Also carried forward from round 3:
  - isLiveConfigDir now requires projects/ to be a DIRECTORY. fileExists is
    true for a regular file of that name, so a stray file could mark a config
    dir live. Covered by a test against a real temp tree — asserting it through
    the injected probe would have proved nothing, since that injection is
    exactly what abstracts the distinction away.
  - Removed a __pycache__/*.pyc committed by an earlier syntax check and
    shipped inside the app bundle; added it to .gitignore.

560 tests, 0 failures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YL1NKtwrKxPSxkBEuKGWNW
@halindrome

Copy link
Copy Markdown
Author

QA Round 3

do:deepseek-v4-pro second-opinion review failed: HTTP 402 Payment Required — all 13 chunks rejected, zero findings produced. Proceeding without it. (Note the shim exited 0 with a non-empty output file, so the skill's own "non-zero exit or empty output" failure rule would not have caught this; a --double round can silently degrade to single-reviewer while reporting as double. Worth fixing in do-reviewer.sh independently of this PR.)

Claude panel found 12 findings: 4 major, 8 minor.

Outcome: the PR has been narrowed rather than patched again

Three rounds, and the major count did not decline — 4, 7, 4. But their location was consistent:

Area Majors R1–R3 Round 3
Resolver + local call sites 2 (both round 1) clean
Remote installer + hook script 5 still producing
Stale-hook sweep + persistence 5 still producing

The fix issue #269 asks for stabilized after round 1. Everything since came from scope I added while responding to review comments.

R3-F1 settled it: my round-2 "fix" to the remote guard put an early return ahead of the pre-existing shutil.which check, making it unreachable. Hosts with Claude Code installed but not yet run would get no hooks where they previously did — a regression against main, in code this PR does not need, that cannot be verified without a remote host.

So 1cbf6d1 removes that scope instead of iterating on it:

  • RemoteInstaller.swift, codeisland-remote-hook.py, RemoteInstallerHookMergeTests → reverted to origin/main
  • cross-directory stale-hook sweep + last-resolved-dir persistence → removed, with its test file

Diff: 16 files / 617 insertions → 12 files / 428. Deferred work is tracked in #271, written up with the specific traps each attempt hit.

Findings resolved by narrowing

R3-F1, F2, F3, F4, F6, F7, F8, F10, F12 all lived in the removed scope. Two were carried forward:

  • R3-F5 (minor, real bug in kept code)isLiveConfigDir used fileExists, which is true for a regular file named projects. A stray file could mark a config dir live. Now requires a directory, verified against a real temp tree:

    old fileExists says live: true   (bug)
    new directoryExists says live: false  (correct)
    

    Asserting this through the injected probe would have proved nothing — the injection is precisely what abstracts the distinction away. I wrote it that way first and caught it before committing.

  • R3-F11 — a __pycache__/*.pyc committed by an earlier local syntax check and shipped inside the app bundle. Removed and gitignored.

R3-F9 remains open by choice: configDir() memoizes on its inputs, so a filesystem-layout change mid-session is missed until inputs change or the app restarts. That matches the Settings copy ("Restart CodeIsland after changing") and invalidateCache() provides the escape hatch, now called from install().

The pattern worth naming

Three of the findings across rounds 2–3 were assertions that could not fail: a String comparison where Swift's canonical equivalence made it vacuous, a test asserting the opposite of its own name, and a persistence mechanism with no executable coverage. Each looked like a test. None was one. I would not have found any of them without an adversarial reviewer explicitly checking whether assertions fail against broken code.


SAST review skipped

Code scanning is not available for wxtsky/CodeIsland. No SAST delta computed — graceful skip, non-blocking.


Schema change: none. Test suite: 560 tests, 0 failures. Rebuilt, reinstalled, re-verified end-to-end: sessions discovered with ~/.claude absent, no preference set, and the app launched via LaunchServices so $CLAUDE_CONFIG_DIR is not inherited — the Finder-launch path this design exists for.

Approval left to the maintainer, as always.


QA performed by Claude Code (claude-opus-4-8)

nguyenvanduocit pushed a commit to nguyenvanduocit/CodeIsland that referenced this pull request Jul 20, 2026
New upstream PR wxtsky#270 (wxtsky/CodeIsland, Jul 19) fixes zero session
detection for users with a custom Claude config directory. Our fork
has the same bug at 8 hardcoded ~/.claude sites. Added T-079 to
kanban (high priority, S effort) and recorded the scout in CLAUDE.md.

GitHub issue creation attempted but GitHub returned persistent 503;
tracked in kanban instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0111nM8vmqbeMaNg6qXPiFB2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No Claude sessions detected when $CLAUDE_CONFIG_DIR is set (~/.claude is hardcoded)

2 participants