Completed
on 9 Jan 2026, 10:35 am

Output

gvShell Multi-Account Architecture

gvShell uses symlink-based account switching (same as konsole), not CLAUDE_CONFIG_DIR. The design is already implemented in gv/gvshell/internal/claude/accounts/.

1. Credential Storage

~/.gvshell/claude/
├── primary/        # Max 20× account
│   └── .credentials.json
├── secondary/      # Max 5× account
│   └── .credentials.json
└── tertiary/       # Optional third
    └── .credentials.json

~/.claude  ~/.gvshell/claude/primary/ (symlink)

2. How Claude CLI is Run

// From claudecli.go - createSession()
cmd := exec.CommandContext(ctx, "claude", [
  "--print",
  "--input-format", "stream-json",
  "--output-format", "stream-json",
  "--model", opts.Model,
  "--dangerously-skip-permissions",  // if configured
])
cmd.Dir = opts.WorkingDir  // Working directory

3. Account Switching Flow

1. KillHotSessionsFunc(ctx)  // Kill cached sessions
2. os.Remove("~/.claude")    // Remove old symlink
3. os.Symlink(target, "~/.claude") // New symlink
4. RestartHotPoolFunc(ctx)   // Restart with new creds

4. Per-Session Account Selection

// Provider can specify which account to use
type Options struct {
  AccountID      string          // "primary", "secondary"
  AccountManager AccountManager  // Handles switch
}

// Before spawning Claude:
if opts.AccountID != "" {
  manager.SwitchAccount(ctx, opts.AccountID)
}

Key Design Decision: Symlink vs CLAUDE_CONFIG_DIR

gvShell does NOT use CLAUDE_CONFIG_DIR. It uses symlink switching like konsole because:

  • Proven pattern from konsole production use
  • Claude CLI reads from ~/.claude by default
  • CLAUDE_CONFIG_DIR has known bugs (IDE integration, local .claude dirs)

Limitation: No Parallel Accounts

With symlink approach, only one account can be active at a time. Using CLAUDE_CONFIG_DIR per-spawn would enable parallel accounts but introduces those bugs.

Source: gvShellDesign.md §4.3.1, accounts/accounts.go, claudecli/claudecli.go

Quick Actions

Original Request

No layout configured

Details

Type General
Status Completed
Scope vibetools
Tags gvshellaccountsarchitecture
Created 9 Jan 2026, 10:35 am
Updated 9 Jan 2026, 10:35 am
Timing
⏱️ 62.9s
0 tools
TTFE 0% Thinking 100% Tools 0%
Created By claude

Raw Data

{
  "id": "6679d0c0-5c3a-48cc-aa7a-338c8d8e569d",
  "type": "general",
  "status": "completed",
  "title": "gvShell Multi-Account Architecture Review",
  "description": "How gvShell handles multi-account credentials and Claude CLI spawning",
  "context": {
    "output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:'Consolas','Monaco',monospace;\">\n<h2 style=\"font-family:sans-serif;color:#fff;margin:0 0 16px 0;\">gvShell Multi-Account Architecture</h2>\n\n<p style=\"line-height:1.6;\">gvShell uses <strong>symlink-based account switching</strong> (same as konsole), not CLAUDE_CONFIG_DIR. The design is already implemented in <code style=\"background:#2d2d2d;padding:2px 6px;border-radius:4px;\">gv/gvshell/internal/claude/accounts/</code>.</p>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">1. Credential Storage</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.6;\">\n<span style=\"color:#56b6c2;\">~/.gvshell/claude/</span>\n├── <span style=\"color:#98c379;\">primary/</span>        <span style=\"color:#7f848e;\"># Max 20× account</span>\n│   └── <span style=\"color:#e5c07b;\">.credentials.json</span>\n├── <span style=\"color:#98c379;\">secondary/</span>      <span style=\"color:#7f848e;\"># Max 5× account</span>\n│   └── <span style=\"color:#e5c07b;\">.credentials.json</span>\n└── <span style=\"color:#98c379;\">tertiary/</span>       <span style=\"color:#7f848e;\"># Optional third</span>\n    └── <span style=\"color:#e5c07b;\">.credentials.json</span>\n\n<span style=\"color:#56b6c2;\">~/.claude</span> <span style=\"color:#c678dd;\">→</span> <span style=\"color:#98c379;\">~/.gvshell/claude/primary/</span> <span style=\"color:#7f848e;\">(symlink)</span>\n</pre>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">2. How Claude CLI is Run</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.6;\">\n<span style=\"color:#7f848e;\">// From claudecli.go - createSession()</span>\ncmd := exec.CommandContext(ctx, <span style=\"color:#98c379;\">\"claude\"</span>, [\n  <span style=\"color:#98c379;\">\"--print\"</span>,\n  <span style=\"color:#98c379;\">\"--input-format\"</span>, <span style=\"color:#98c379;\">\"stream-json\"</span>,\n  <span style=\"color:#98c379;\">\"--output-format\"</span>, <span style=\"color:#98c379;\">\"stream-json\"</span>,\n  <span style=\"color:#98c379;\">\"--model\"</span>, opts.Model,\n  <span style=\"color:#98c379;\">\"--dangerously-skip-permissions\"</span>,  <span style=\"color:#7f848e;\">// if configured</span>\n])\ncmd.Dir = opts.WorkingDir  <span style=\"color:#7f848e;\">// Working directory</span>\n</pre>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">3. Account Switching Flow</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.8;\">\n<span style=\"color:#e5c07b;\">1.</span> <span style=\"color:#c678dd;\">KillHotSessionsFunc(ctx)</span>  <span style=\"color:#7f848e;\">// Kill cached sessions</span>\n<span style=\"color:#e5c07b;\">2.</span> <span style=\"color:#c678dd;\">os.Remove(\"~/.claude\")</span>    <span style=\"color:#7f848e;\">// Remove old symlink</span>\n<span style=\"color:#e5c07b;\">3.</span> <span style=\"color:#c678dd;\">os.Symlink(target, \"~/.claude\")</span> <span style=\"color:#7f848e;\">// New symlink</span>\n<span style=\"color:#e5c07b;\">4.</span> <span style=\"color:#c678dd;\">RestartHotPoolFunc(ctx)</span>   <span style=\"color:#7f848e;\">// Restart with new creds</span>\n</pre>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">4. Per-Session Account Selection</h3>\n<pre style=\"background:#2d2d2d;padding:12px;border-radius:6px;line-height:1.6;\">\n<span style=\"color:#7f848e;\">// Provider can specify which account to use</span>\ntype Options struct {\n  AccountID      <span style=\"color:#c678dd;\">string</span>          <span style=\"color:#7f848e;\">// \"primary\", \"secondary\"</span>\n  AccountManager <span style=\"color:#c678dd;\">AccountManager</span>  <span style=\"color:#7f848e;\">// Handles switch</span>\n}\n\n<span style=\"color:#7f848e;\">// Before spawning Claude:</span>\n<span style=\"color:#c678dd;\">if</span> opts.AccountID != \"\" {\n  manager.SwitchAccount(ctx, opts.AccountID)\n}\n</pre>\n\n<h3 style=\"color:#f97316;margin:16px 0 8px 0;\">Key Design Decision: Symlink vs CLAUDE_CONFIG_DIR</h3>\n<p style=\"line-height:1.6;\">gvShell <strong>does NOT use CLAUDE_CONFIG_DIR</strong>. It uses symlink switching like konsole because:</p>\n<ul style=\"margin:0;padding-left:20px;line-height:1.8;\">\n<li>Proven pattern from konsole production use</li>\n<li>Claude CLI reads from <code style=\"background:#2d2d2d;padding:2px 6px;border-radius:4px;\">~/.claude</code> by default</li>\n<li>CLAUDE_CONFIG_DIR has known bugs (IDE integration, local .claude dirs)</li>\n</ul>\n\n<h3 style=\"color:#61afef;margin:16px 0 8px 0;\">Limitation: No Parallel Accounts</h3>\n<p style=\"line-height:1.6;\">With symlink approach, only <strong>one account can be active at a time</strong>. Using CLAUDE_CONFIG_DIR per-spawn would enable parallel accounts but introduces those bugs.</p>\n\n<p style=\"margin:16px 0 0 0;color:#7f848e;\">Source: gvShellDesign.md §4.3.1, accounts/accounts.go, claudecli/claudecli.go</p>\n</div>",
    "requestedAt": "2026-01-09T00:20:00.000Z",
    "requestId": "b1a67c8c-b3b6-46b9-83a3-7df6b987f57e",
    "choices": [
      {
        "label": "Test CLAUDE_CONFIG_DIR",
        "value": "Run a test to see if CLAUDE_CONFIG_DIR works reliably for spawning Claude with different configs in parallel",
        "primary": true
      },
      {
        "label": "Keep symlink approach",
        "value": "The symlink approach is working well in konsole, stick with it for gvShell"
      },
      {
        "label": "Hybrid approach",
        "value": "Use symlink for hot pool but CLAUDE_CONFIG_DIR for ad-hoc spawns"
      }
    ],
    "turnTiming": {
      "totalMs": 62918,
      "ttfeMs": 53,
      "thinkingMs": 62865,
      "toolExecutionMs": 0,
      "toolCallCount": 0,
      "thinkingPct": 100,
      "toolsPct": 0,
      "ttfePct": 0
    }
  },
  "createdBy": "claude",
  "createdAt": "2026-01-09T00:35:29.317Z",
  "updatedAt": "2026-01-09T00:35:35.771Z",
  "requestId": "b1a67c8c-b3b6-46b9-83a3-7df6b987f57e",
  "scope": "vibetools",
  "tags": [
    "gvshell",
    "accounts",
    "architecture"
  ],
  "targetUser": "claude"
}
DashboardReportsKontasksFlowsDecisionsSessionsTelemetryLogs + Go