Skip to content

fix(claude): honor CLAUDE_CONFIG_DIR when listing session history#374

Merged
mbektas merged 1 commit into
plmbr:mainfrom
pjdoland:fix/373-sessions-claude-config-dir
Jun 16, 2026
Merged

fix(claude): honor CLAUDE_CONFIG_DIR when listing session history#374
mbektas merged 1 commit into
plmbr:mainfrom
pjdoland:fix/373-sessions-claude-config-dir

Conversation

@pjdoland

Copy link
Copy Markdown
Collaborator

Summary

The Claude CLI relocates its entire config dir, including the session transcripts under projects/, when CLAUDE_CONFIG_DIR is set. NBI's session history surfaces (the chat-sidebar resume picker and the launcher tile's session list) always read ~/.claude/projects, so on deployments that set the override (JupyterHub images commonly point it at a persistent volume, e.g. /home/jovyan/workspace/.claude) both pickers came up empty even though claude --resume saw every session.

Solution

The load-bearing change is in claude_sessions.py: get_sessions_dir and list_all_sessions now default claude_home to a new util.get_claude_config_dir() helper (CLAUDE_CONFIG_DIR when set, else ~/.claude) instead of a hardcoded Path.home() / ".claude". The skills directory (config.py) and spinner-verbs lookup (extension.py) already carried their own inline copies of the same expression; they now route through the helper too, so the override semantics cannot drift between surfaces.

Two deliberate choices, both verified against the CLI's behavior: the helper does not expanduser the env value (the CLI doesn't expand ~ either, so a literal-tilde value should fail identically in both), and an empty string counts as unset (matching the CLI's Node-style falsy check). The docs filesystem table and the troubleshooting diagnostic listing now mention the override, since ls ~/.claude/projects/ is exactly the misleading step on an affected deployment.

Testing

  • Four new tests in tests/test_claude_sessions.py (TestClaudeConfigDirDefault): get_sessions_dir honors the env var and falls back to the home dir (HOME and USERPROFILE both pinned, so the assertion holds on POSIX and Windows); list_all_sessions discovers transcripts under $CLAUDE_CONFIG_DIR/projects with no claude_home argument (the exact production call shape); and a negative test proving ~/.claude is not consulted when the override is set. All existing tests pass claude_home explicitly, so the suite stays insulated from a developer's exported CLAUDE_CONFIG_DIR (verified by running the full file with a junk value exported).
  • Full verification green: pytest (1177 plus the claude_client suite separately), tsc --noEmit, lint:check, jest (352).
  • Live verification: started JupyterLab with CLAUDE_CONFIG_DIR pointing at a synthetic transcript store; GET /notebook-intelligence/claude-sessions returned exactly the synthetic session for both scope=all and scope=cwd, and none of the real sessions in the actual ~/.claude/projects, confirming the override replaces the home dir the same way the CLI does.
  • The SDK subprocess inherits os.environ, so NBI-created Claude sessions land in the same override dir the picker now reads, and Jupyter terminals inherit the server env, so the generated claude --resume command finds them.

Risks / follow-ups

  • No behavior change when CLAUDE_CONFIG_DIR is unset: the helper's fallback is the same ~/.claude path as before (via expanduser, which Path.home() also uses).
  • Two adjacent surfaces have the same class of gap but need different relocation logic, so they are excluded here and tracked separately: claude_mcp_manager.py reads ~/.claude.json (the CLI moves that file inside $CLAUDE_CONFIG_DIR when set, but its default location is the home dir itself, not ~/.claude), and plugin_manager.py falls back to ~/.claude/plugins for the plugin cache.

Fixes #373

The Claude CLI relocates its entire config dir, including session
transcripts under projects/, when CLAUDE_CONFIG_DIR is set. The session
pickers always read ~/.claude/projects, so on deployments that set the
override (JupyterHub images commonly point it at a persistent volume)
both pickers came up empty even though 'claude --resume' saw every
session.

Resolve the CLI's config dir through one shared util helper and make it
the claude_home default for get_sessions_dir and list_all_sessions. The
skills directory and spinner-verbs lookups already carried their own
copies of the same expression; they now use the helper too, so the
override semantics can't drift between surfaces. The helper deliberately
does not expanduser the env value (the CLI doesn't) and treats an empty
string as unset (matching the CLI's Node-style falsy check).

The MCP user config (~/.claude.json) and the plugin cache have the same
class of gap but need different relocation logic; tracked separately.

Fixes plmbr#373
@pjdoland pjdoland force-pushed the fix/373-sessions-claude-config-dir branch from 85908ce to e0a9fd5 Compare June 16, 2026 10:15
@pjdoland

Copy link
Copy Markdown
Collaborator Author

Resolved conflicts.

@mbektas mbektas merged commit 7e6f961 into plmbr:main Jun 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude session history is empty when CLAUDE_CONFIG_DIR is set

2 participants