Skip to content

Support $CLAUDE_CONFIG_DIR for custom config paths #349

Closed

Description

@zehua

Problem

Claude Code supports $CLAUDE_CONFIG_DIR to override the default ~/.claude directory. However, gstack hardcodes ~/.claude/skills/gstack throughout the codebase (480 occurrences across 50 files). When a user runs Claude Code with a custom $CLAUDE_CONFIG_DIR, gstack silently references the default ~/.claude directory instead — reading the wrong memories, settings, and project config, and writing state to the wrong location.

Scope of affected files

Source-of-truth files (7 — must change)

File Role
scripts/gen-skill-docs.ts Defines skillRoot, binDir, browseDir on lines 42-45; embeds paths into all generated preamble/postamble templates
setup Install script — creates symlinks in ~/.claude/skills/, rewrites paths for kiro/codex
scripts/skill-check.ts Validates generated output contains correct host paths
browse/src/find-browse.ts Binary discovery — searches ~/.claude/skills/gstack/browse/dist/browse
bin/gstack-global-discover.ts Session discovery across ~/.claude/projects
bin/dev-setup Creates .claude/skills/gstack dev symlink
bin/dev-teardown Cleans up dev symlinks

Template files with their own hardcoded paths (~17 .tmpl files)

Several templates (notably gstack-upgrade/SKILL.md.tmpl with 19 refs, codex/SKILL.md.tmpl with 5, autoplan/SKILL.md.tmpl with 7) contain literal ~/.claude/skills/gstack in bash blocks beyond what the generator injects. These need fixing too.

Auto-fixed (no direct changes needed)

28 generated SKILL.md files — regenerated from templates when sources are fixed.

Also need updates

  • 6 test files (verify path correctness in generated output)
  • 8 documentation files (CLAUDE.md, ARCHITECTURE.md, etc.)

Proposed fix

Replace hardcoded ~/.claude with ${CLAUDE_CONFIG_DIR:-$HOME/.claude} at each layer:

  1. gen-skill-docs.ts — change skillRoot, localSkillRoot, binDir, browseDir to use the dynamic pattern
  2. setup — resolve install target via ${CLAUDE_CONFIG_DIR:-$HOME/.claude}
  3. find-browse.ts — check $CLAUDE_CONFIG_DIR when searching for the browse binary
  4. bin/dev-setup, bin/dev-teardown — same pattern
  5. .tmpl files — replace literal ~/.claude/skills/gstack with the dynamic pattern in bash blocks
  6. Regenerate all SKILL.md files

Relation to #289

Issue #289 (host integration contract RFC) proposes a broader abstraction for multi-host support. $CLAUDE_CONFIG_DIR support is a concrete, narrower fix that can land independently — it fixes a real bug for Claude Code users today without waiting for the full host adapter architecture. The fix naturally aligns with #289's direction since it makes path resolution dynamic rather than hardcoded.

Impact

Users running Claude Code with a custom $CLAUDE_CONFIG_DIR will have gstack silently reference the default ~/.claude directory. This means gstack reads the wrong memories, settings, and project config — and writes state (sessions, analytics, skill data) to the wrong location. No error or warning is shown.

Activity

  1. loocor commented on Mar 23, 2026

    @loocor

    Thanks for writing this up so concretely. I now have PR #364 open as the phase-1 install/runtime tranche of RFC #289:
    #364

    That PR does not add $CLAUDE_CONFIG_DIR support yet, but it does pull host layouts, discovery roots, and runtime roots into a single contract (scripts/host-registry.ts) instead of leaving them scattered across setup/templates/checks.

    So to me this issue now looks like a strong, narrow follow-up on top of that contract: config-root resolution should become part of the host path model rather than another repo-wide literal-path cleanup.

  2. zehua commented on Mar 23, 2026

    @zehua
    Author

    @loocor let's figure out if the proposed fix in #351 could be merged first or be done on top of #364

  3. loocor commented on Mar 24, 2026

    @loocor

    @loocor let's figure out if the proposed fix in #351 could be merged first or be done on top of #364

    The changes are huge, and I guess Garry won't merge it.

  4. mkyed commented on Apr 28, 2026

    @mkyed

    Until this lands, here's a workaround that auto-applies on every /gstack-upgrade. Sharing in case it helps anyone running with CLAUDE_CONFIG_DIR set:

    A git post-merge hook in each gstack repo runs an idempotent substitution over SKILL.md and SKILL.md.tmpl, replacing ~/.claude/skills/gstack/ with the active context's path. Because the hook lives in .git/hooks/, it survives git pull and fires automatically after /gstack-upgrade.

    Script — drop this somewhere on your PATH (e.g. ~/bin/gstack-fix-paths):

    #!/usr/bin/env bash
    # Idempotent. Auto-detects context from gstack repo location.
    # Workaround for #349 / #351 until merged.
    set -euo pipefail
    
    resolve_context() {
      if [ -n "${1:-}" ]; then
        if [ -f "$1/setup" ] && [ -d "$1/bin" ] && [ -d "$1/agents" ]; then
          cd "$1/../.." && pwd -P
        else
          cd "$1" && pwd -P
        fi
        return
      fi
      local cur; cur="$(pwd -P)"
      while [ "$cur" != "/" ]; do
        if [ -f "$cur/setup" ] && [ -d "$cur/bin" ] && [ -d "$cur/.git" ]; then
          cd "$cur/../.." && pwd -P; return
        fi
        cur="$(dirname "$cur")"
      done
      echo "Could not determine context — pass gstack repo path." >&2
      return 1
    }
    
    CTX_ABS="$(resolve_context "${1:-}")"
    GSTACK_DIR="$CTX_ABS/skills/gstack"
    HOME_PREFIX="${HOME%/}/"
    TILDE_CTX="${CTX_ABS#$HOME_PREFIX}"
    [ "$TILDE_CTX" != "$CTX_ABS" ] && TILDE_CTX="~/$TILDE_CTX" || TILDE_CTX="$CTX_ABS"
    REPLACE="${TILDE_CTX}/skills/gstack/"
    
    scanned=0; patched=0
    while IFS= read -r f; do
      scanned=$((scanned + 1))
      if grep -qF '~/.claude/skills/gstack/' "$f" 2>/dev/null; then
        REPLACE="$REPLACE" perl -pi -e 's|\Q~/.claude/skills/gstack/\E|$ENV{REPLACE}|g' "$f"
        patched=$((patched + 1))
      fi
    done < <(find "$GSTACK_DIR" \( -name 'SKILL.md' -o -name 'SKILL.md.tmpl' \) -type f)
    
    echo "[gstack-fix-paths] context=$TILDE_CTX scanned=$scanned patched=$patched"

    Hook — drop this in each gstack repo's .git/hooks/post-merge, mode 755:

    #!/bin/sh
    exec "$HOME/bin/gstack-fix-paths" "$(git rev-parse --show-toplevel)"

    Verified end-to-end on v1.15.0.0: rolled back to v1.14.0.0, artificially regressed pair-agent/SKILL.md (45 hardcoded refs), ran git pull --ff-only origin main, hook fired automatically and patched 62 SKILL.md files. Idempotent — re-run patches 0.

    Tested with two parallel contexts (~/Work/.claude and ~/Personal/.claude). Uses perl -pi so it works on both BSD/macOS and GNU/Linux without the sed -i syntax difference.

  5. elifarley commented on Jun 10, 2026

    @elifarley

    Confirming this still bites on v1.57.9.0, with the most common user-visible failure mode in case it helps prioritize:

    Under a custom CLAUDE_CONFIG_DIR, /autoplan and /codex print [codex-unavailable: auth missing] on every run even with Codex authenticated. The hardcoded source ~/.claude/skills/gstack/bin/gstack-codex-probe can't resolve, so _gstack_codex_auth_probe is never defined and ! <undefined-cmd> (exit 127) reads as an auth failure. The same mechanism silently degrades the gstack-config / telemetry / learnings calls.

    Two small notes for whoever picks this up:

    Happy to help test whichever approach lands.

  6. time-attack commented on Jul 15, 2026

    @time-attack
    Contributor

    @zehua CLAUDE_CONFIG_DIR remains unsupported by the runtime path contract, and the current pull requests do not implement it safely.

  7. capy-ai commented on Oct 11, 2026

    @capy-ai
    Contributor

    Thanks @zehua and everyone who confirmed this. Installs under a custom CLAUDE_CONFIG_DIR now get their own rendered skills that point at the right root (v1.91.16.0, #1882), so the /autoplan and /codex codex-probe failures from hardcoded ~/.claude paths are gone. One small leftover (setup run from outside a skills dir) is tracked in TODOS.md; please upgrade and reopen if you still see wrong-path behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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