@ghackk/multi-claude 1.0.10 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/claude-menu.ps1 CHANGED
@@ -7,6 +7,89 @@ $SHARED_PLUGINS_DIR = "$SHARED_DIR\plugins"
7
7
  $SHARED_MARKETPLACES_DIR = "$SHARED_PLUGINS_DIR\marketplaces"
8
8
  $PAIR_SERVER = "https://pair.ghackk.com"
9
9
 
10
+ # ─── ENSURE CLAUDE CLI INSTALLED ────────────────────────────────────────────
11
+
12
+ function Ensure-ClaudeInstalled {
13
+ if (Get-Command claude -ErrorAction SilentlyContinue) { return }
14
+
15
+ Write-Host ""
16
+ Write-Host " Claude CLI is not installed." -ForegroundColor Yellow
17
+ Write-Host ""
18
+ Write-Host " Detected system:" -ForegroundColor Cyan
19
+ Write-Host " OS: Windows $([System.Environment]::OSVersion.Version)" -ForegroundColor White
20
+ Write-Host " Arch: $env:PROCESSOR_ARCHITECTURE" -ForegroundColor White
21
+
22
+ if (Get-Command winget -ErrorAction SilentlyContinue) {
23
+ Write-Host " Pkg: WinGet" -ForegroundColor White
24
+ }
25
+ if (Get-Command scoop -ErrorAction SilentlyContinue) {
26
+ Write-Host " Pkg: Scoop" -ForegroundColor White
27
+ }
28
+ if (Get-Command npm -ErrorAction SilentlyContinue) {
29
+ Write-Host " Pkg: npm" -ForegroundColor White
30
+ }
31
+
32
+ Write-Host ""
33
+ $choice = Read-Host " Install Claude CLI now? (y/n)"
34
+ if ($choice -ne "y") {
35
+ Write-Host " Claude CLI is required. Exiting." -ForegroundColor Red
36
+ exit 1
37
+ }
38
+
39
+ Write-Host ""
40
+ Write-Host " Installing Claude CLI..." -ForegroundColor Gray
41
+
42
+ # Try native installer first
43
+ try {
44
+ Write-Host " Trying native installer..." -ForegroundColor Gray
45
+ Invoke-Expression (Invoke-RestMethod -Uri "https://claude.ai/install.ps1" -ErrorAction Stop)
46
+ # Refresh PATH
47
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "User") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
48
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
49
+ Write-Host " Claude CLI installed successfully!" -ForegroundColor Green
50
+ return
51
+ }
52
+ } catch {
53
+ Write-Host " Native installer failed: $_" -ForegroundColor Gray
54
+ }
55
+
56
+ # Try WinGet
57
+ if (Get-Command winget -ErrorAction SilentlyContinue) {
58
+ Write-Host " Trying WinGet..." -ForegroundColor Gray
59
+ try {
60
+ winget install Anthropic.ClaudeCode --accept-source-agreements --accept-package-agreements 2>$null
61
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "User") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
62
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
63
+ Write-Host " Claude CLI installed via WinGet!" -ForegroundColor Green
64
+ return
65
+ }
66
+ } catch {}
67
+ }
68
+
69
+ # Try npm
70
+ if (Get-Command npm -ErrorAction SilentlyContinue) {
71
+ Write-Host " Trying npm..." -ForegroundColor Gray
72
+ try {
73
+ npm install -g @anthropic-ai/claude-code 2>$null
74
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
75
+ Write-Host " Claude CLI installed via npm!" -ForegroundColor Green
76
+ return
77
+ }
78
+ } catch {}
79
+ }
80
+
81
+ Write-Host ""
82
+ Write-Host " Automatic install failed." -ForegroundColor Red
83
+ Write-Host " Manual install options:" -ForegroundColor White
84
+ Write-Host " irm https://claude.ai/install.ps1 | iex" -ForegroundColor Gray
85
+ Write-Host " winget install Anthropic.ClaudeCode" -ForegroundColor Gray
86
+ Write-Host " npm install -g @anthropic-ai/claude-code" -ForegroundColor Gray
87
+ Write-Host ""
88
+ pause
89
+ }
90
+
91
+ Ensure-ClaudeInstalled
92
+
10
93
  # ─── ENSURE DIRECTORIES EXIST ───────────────────────────────────────────────
11
94
 
12
95
  if (!(Test-Path $ACCOUNTS_DIR)) { New-Item -ItemType Directory -Path $ACCOUNTS_DIR | Out-Null }
@@ -582,7 +665,10 @@ function Build-ExportToken($name) {
582
665
  }
583
666
 
584
667
  Copy-Item $selected.FullName "$tempDir\launcher.bat"
585
- $name | Out-File "$tempDir\profile-name.txt" -Encoding UTF8 -NoNewline
668
+ # Also create Unix launcher for cross-platform compatibility
669
+ $shContent = "#!/bin/bash`nexport CLAUDE_CONFIG_DIR=`"`$HOME/.$name`"`nclaude `"`$@`""
670
+ [System.IO.File]::WriteAllText("$tempDir\launcher.sh", $shContent, (New-Object System.Text.UTF8Encoding $false))
671
+ [System.IO.File]::WriteAllText("$tempDir\profile-name.txt", $name, (New-Object System.Text.UTF8Encoding $false))
586
672
 
587
673
  $zipPath = "$env:TEMP\claude-export-$name.zip"
588
674
  if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
@@ -661,7 +747,11 @@ function Apply-ImportToken($token) {
661
747
  Remove-Item $zipPath -Force
662
748
  return $false
663
749
  }
664
- $name = (Get-Content $nameFile -Raw).Trim()
750
+ $nameRaw = [System.IO.File]::ReadAllBytes($nameFile)
751
+ if ($nameRaw.Length -ge 3 -and $nameRaw[0] -eq 0xEF -and $nameRaw[1] -eq 0xBB -and $nameRaw[2] -eq 0xBF) {
752
+ $nameRaw = $nameRaw[3..($nameRaw.Length-1)]
753
+ }
754
+ $name = [System.Text.Encoding]::UTF8.GetString($nameRaw).Trim()
665
755
 
666
756
  Write-Host ""
667
757
  Write-Host " Detected profile: $name" -ForegroundColor Cyan
@@ -694,12 +784,16 @@ function Apply-ImportToken($token) {
694
784
  Write-Host " Profile restored (credentials, settings, session)" -ForegroundColor Green
695
785
 
696
786
  $launcherSrc = "$extractDir\launcher.bat"
697
- $launcherDest = "$ACCOUNTS_DIR\$name.bat"
698
- if (Test-Path $launcherSrc) {
699
- if (!(Test-Path $ACCOUNTS_DIR)) { New-Item -ItemType Directory -Path $ACCOUNTS_DIR | Out-Null }
700
- Copy-Item $launcherSrc $launcherDest -Force
701
- Write-Host " Launcher created" -ForegroundColor Green
787
+ if (!(Test-Path $launcherSrc)) {
788
+ # Cross-platform: generate .bat from profile name if only .sh exists
789
+ $batContent = "@echo off`r`nset CLAUDE_CONFIG_DIR=%USERPROFILE%\.$name`r`nclaude %*"
790
+ $launcherSrc = "$extractDir\launcher.bat"
791
+ [System.IO.File]::WriteAllText($launcherSrc, $batContent)
702
792
  }
793
+ $launcherDest = "$ACCOUNTS_DIR\$name.bat"
794
+ if (!(Test-Path $ACCOUNTS_DIR)) { New-Item -ItemType Directory -Path $ACCOUNTS_DIR | Out-Null }
795
+ Copy-Item $launcherSrc $launcherDest -Force
796
+ Write-Host " Launcher created" -ForegroundColor Green
703
797
 
704
798
  Write-Host ""
705
799
  Write-Host " Profile '$name' imported successfully!" -ForegroundColor Green
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghackk/multi-claude",
3
- "version": "1.0.10",
3
+ "version": "1.0.13",
4
4
  "description": "Run multiple Claude CLI accounts with shared settings, plugins, marketplace sync, and backup/restore",
5
5
  "bin": {
6
6
  "multi-claude": "bin/claude-multi.js"
@@ -10,6 +10,88 @@ SHARED_PLUGINS_DIR="$SHARED_DIR/plugins"
10
10
  SHARED_MARKETPLACES_DIR="$SHARED_PLUGINS_DIR/marketplaces"
11
11
  PAIR_SERVER="https://pair.ghackk.com"
12
12
 
13
+ # ─── ENSURE CLAUDE CLI INSTALLED ──────────────────────────────────────────
14
+
15
+ ensure_claude_installed() {
16
+ command -v claude &>/dev/null && return 0
17
+
18
+ echo ""
19
+ echo -e " \033[33mClaude CLI is not installed.\033[0m"
20
+ echo ""
21
+ echo -e " \033[36mDetected system:\033[0m"
22
+
23
+ local os="" arch="" pkg_manager=""
24
+ os=$(uname -s)
25
+ arch=$(uname -m)
26
+ echo -e " OS: $os"
27
+ echo -e " Arch: $arch"
28
+
29
+ # Detect package manager
30
+ if command -v brew &>/dev/null; then
31
+ pkg_manager="brew"
32
+ echo -e " Pkg: Homebrew"
33
+ elif command -v apt &>/dev/null; then
34
+ pkg_manager="apt"
35
+ echo -e " Pkg: apt"
36
+ elif command -v dnf &>/dev/null; then
37
+ pkg_manager="dnf"
38
+ echo -e " Pkg: dnf"
39
+ elif command -v yum &>/dev/null; then
40
+ pkg_manager="yum"
41
+ echo -e " Pkg: yum"
42
+ elif command -v pacman &>/dev/null; then
43
+ pkg_manager="pacman"
44
+ echo -e " Pkg: pacman"
45
+ fi
46
+
47
+ echo ""
48
+ echo -e " \033[37mInstall Claude CLI now? (y/n)\033[0m"
49
+ read -p " > " install_choice
50
+
51
+ if [ "$install_choice" != "y" ] && [ "$install_choice" != "Y" ]; then
52
+ echo -e " \033[31mClaude CLI is required. Exiting.\033[0m"
53
+ exit 1
54
+ fi
55
+
56
+ echo ""
57
+ echo -e " \033[90mInstalling Claude CLI...\033[0m"
58
+
59
+ # Try native installer first (works on all platforms)
60
+ if curl -fsSL https://claude.ai/install.sh | bash; then
61
+ echo ""
62
+ echo -e " \033[32mClaude CLI installed successfully!\033[0m"
63
+ # Reload PATH
64
+ export PATH="$HOME/.claude/bin:$HOME/.local/bin:$PATH"
65
+ if command -v claude &>/dev/null; then
66
+ echo -e " \033[32mclaude version: $(claude --version 2>/dev/null || echo 'installed')\033[0m"
67
+ return 0
68
+ fi
69
+ fi
70
+
71
+ # Fallback: try npm
72
+ if command -v npm &>/dev/null; then
73
+ echo -e " \033[90mTrying npm install...\033[0m"
74
+ npm install -g @anthropic-ai/claude-code 2>/dev/null && {
75
+ echo -e " \033[32mClaude CLI installed via npm!\033[0m"
76
+ return 0
77
+ }
78
+ fi
79
+
80
+ echo -e " \033[31mAutomatic install failed.\033[0m"
81
+ echo -e " \033[37mManual install options:\033[0m"
82
+ echo -e " curl -fsSL https://claude.ai/install.sh | bash"
83
+ echo -e " npm install -g @anthropic-ai/claude-code"
84
+ echo -e " brew install --cask claude-code"
85
+ echo ""
86
+ read -p " Press Enter to continue anyway..." _
87
+ }
88
+
89
+ ensure_claude_installed
90
+
91
+ # ─── ENSURE DIRECTORIES EXIST ────────────────────────────────────────────
92
+
93
+ mkdir -p "$ACCOUNTS_DIR" "$BACKUP_DIR"
94
+
13
95
  # ─── DISPLAY ────────────────────────────────────────────────────────────────
14
96
 
15
97
  show_header() {
@@ -576,6 +658,8 @@ build_export_token() {
576
658
  fi
577
659
 
578
660
  [ -f "$ACCOUNTS_DIR/$name.sh" ] && cp "$ACCOUNTS_DIR/$name.sh" "$tempDir/launcher.sh"
661
+ # Also create Windows launcher for cross-platform compatibility
662
+ printf "@echo off\r\nset CLAUDE_CONFIG_DIR=%%USERPROFILE%%\\.%s\r\nclaude %%*\r\n" "$name" > "$tempDir/launcher.bat"
579
663
  echo -n "$name" > "$tempDir/profile-name.txt"
580
664
 
581
665
  local zipPath=$(mktemp)
@@ -659,7 +743,12 @@ apply_import_token() {
659
743
  rm -rf "$tempDir"
660
744
  return 1
661
745
  fi
662
- local name=$(cat "$nameFile" | tr -d '\r\n')
746
+ local name=$(python3 -c "
747
+ import sys
748
+ data = open('$nameFile', 'rb').read()
749
+ if data[:3] == b'\xef\xbb\xbf': data = data[3:]
750
+ sys.stdout.write(data.decode('utf-8').strip())
751
+ " 2>/dev/null || cat "$nameFile" | tr -d '\r\n')
663
752
 
664
753
  if ! echo "$name" | grep -qE '^[a-zA-Z0-9_-]+$'; then
665
754
  echo -e " \033[31mInvalid profile name in token.\033[0m"
@@ -692,11 +781,16 @@ apply_import_token() {
692
781
  cp -r "$importConfig"/. "$configDir/"
693
782
  echo -e " \033[32mProfile restored (credentials, settings, session)\033[0m"
694
783
 
784
+ mkdir -p "$ACCOUNTS_DIR"
695
785
  if [ -f "$tempDir/launcher.sh" ]; then
696
786
  cp "$tempDir/launcher.sh" "$ACCOUNTS_DIR/$name.sh"
697
787
  chmod +x "$ACCOUNTS_DIR/$name.sh"
698
- echo -e " \033[32mLauncher created\033[0m"
788
+ else
789
+ # Cross-platform: generate .sh from profile name if only .bat exists
790
+ printf '#!/bin/bash\nexport CLAUDE_CONFIG_DIR="$HOME/.%s"\nclaude "$@"\n' "$name" > "$ACCOUNTS_DIR/$name.sh"
791
+ chmod +x "$ACCOUNTS_DIR/$name.sh"
699
792
  fi
793
+ echo -e " \033[32mLauncher created\033[0m"
700
794
 
701
795
  echo ""
702
796
  echo -e " \033[32mProfile '$name' imported successfully!\033[0m"
@@ -7,6 +7,89 @@ $SHARED_PLUGINS_DIR = "$SHARED_DIR\plugins"
7
7
  $SHARED_MARKETPLACES_DIR = "$SHARED_PLUGINS_DIR\marketplaces"
8
8
  $PAIR_SERVER = "https://pair.ghackk.com"
9
9
 
10
+ # ─── ENSURE CLAUDE CLI INSTALLED ────────────────────────────────────────────
11
+
12
+ function Ensure-ClaudeInstalled {
13
+ if (Get-Command claude -ErrorAction SilentlyContinue) { return }
14
+
15
+ Write-Host ""
16
+ Write-Host " Claude CLI is not installed." -ForegroundColor Yellow
17
+ Write-Host ""
18
+ Write-Host " Detected system:" -ForegroundColor Cyan
19
+ Write-Host " OS: Windows $([System.Environment]::OSVersion.Version)" -ForegroundColor White
20
+ Write-Host " Arch: $env:PROCESSOR_ARCHITECTURE" -ForegroundColor White
21
+
22
+ if (Get-Command winget -ErrorAction SilentlyContinue) {
23
+ Write-Host " Pkg: WinGet" -ForegroundColor White
24
+ }
25
+ if (Get-Command scoop -ErrorAction SilentlyContinue) {
26
+ Write-Host " Pkg: Scoop" -ForegroundColor White
27
+ }
28
+ if (Get-Command npm -ErrorAction SilentlyContinue) {
29
+ Write-Host " Pkg: npm" -ForegroundColor White
30
+ }
31
+
32
+ Write-Host ""
33
+ $choice = Read-Host " Install Claude CLI now? (y/n)"
34
+ if ($choice -ne "y") {
35
+ Write-Host " Claude CLI is required. Exiting." -ForegroundColor Red
36
+ exit 1
37
+ }
38
+
39
+ Write-Host ""
40
+ Write-Host " Installing Claude CLI..." -ForegroundColor Gray
41
+
42
+ # Try native installer first
43
+ try {
44
+ Write-Host " Trying native installer..." -ForegroundColor Gray
45
+ Invoke-Expression (Invoke-RestMethod -Uri "https://claude.ai/install.ps1" -ErrorAction Stop)
46
+ # Refresh PATH
47
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "User") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
48
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
49
+ Write-Host " Claude CLI installed successfully!" -ForegroundColor Green
50
+ return
51
+ }
52
+ } catch {
53
+ Write-Host " Native installer failed: $_" -ForegroundColor Gray
54
+ }
55
+
56
+ # Try WinGet
57
+ if (Get-Command winget -ErrorAction SilentlyContinue) {
58
+ Write-Host " Trying WinGet..." -ForegroundColor Gray
59
+ try {
60
+ winget install Anthropic.ClaudeCode --accept-source-agreements --accept-package-agreements 2>$null
61
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "User") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
62
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
63
+ Write-Host " Claude CLI installed via WinGet!" -ForegroundColor Green
64
+ return
65
+ }
66
+ } catch {}
67
+ }
68
+
69
+ # Try npm
70
+ if (Get-Command npm -ErrorAction SilentlyContinue) {
71
+ Write-Host " Trying npm..." -ForegroundColor Gray
72
+ try {
73
+ npm install -g @anthropic-ai/claude-code 2>$null
74
+ if (Get-Command claude -ErrorAction SilentlyContinue) {
75
+ Write-Host " Claude CLI installed via npm!" -ForegroundColor Green
76
+ return
77
+ }
78
+ } catch {}
79
+ }
80
+
81
+ Write-Host ""
82
+ Write-Host " Automatic install failed." -ForegroundColor Red
83
+ Write-Host " Manual install options:" -ForegroundColor White
84
+ Write-Host " irm https://claude.ai/install.ps1 | iex" -ForegroundColor Gray
85
+ Write-Host " winget install Anthropic.ClaudeCode" -ForegroundColor Gray
86
+ Write-Host " npm install -g @anthropic-ai/claude-code" -ForegroundColor Gray
87
+ Write-Host ""
88
+ pause
89
+ }
90
+
91
+ Ensure-ClaudeInstalled
92
+
10
93
  # ─── ENSURE DIRECTORIES EXIST ───────────────────────────────────────────────
11
94
 
12
95
  if (!(Test-Path $ACCOUNTS_DIR)) { New-Item -ItemType Directory -Path $ACCOUNTS_DIR | Out-Null }
@@ -582,7 +665,10 @@ function Build-ExportToken($name) {
582
665
  }
583
666
 
584
667
  Copy-Item $selected.FullName "$tempDir\launcher.bat"
585
- $name | Out-File "$tempDir\profile-name.txt" -Encoding UTF8 -NoNewline
668
+ # Also create Unix launcher for cross-platform compatibility
669
+ $shContent = "#!/bin/bash`nexport CLAUDE_CONFIG_DIR=`"`$HOME/.$name`"`nclaude `"`$@`""
670
+ [System.IO.File]::WriteAllText("$tempDir\launcher.sh", $shContent, (New-Object System.Text.UTF8Encoding $false))
671
+ [System.IO.File]::WriteAllText("$tempDir\profile-name.txt", $name, (New-Object System.Text.UTF8Encoding $false))
586
672
 
587
673
  $zipPath = "$env:TEMP\claude-export-$name.zip"
588
674
  if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
@@ -661,7 +747,11 @@ function Apply-ImportToken($token) {
661
747
  Remove-Item $zipPath -Force
662
748
  return $false
663
749
  }
664
- $name = (Get-Content $nameFile -Raw).Trim()
750
+ $nameRaw = [System.IO.File]::ReadAllBytes($nameFile)
751
+ if ($nameRaw.Length -ge 3 -and $nameRaw[0] -eq 0xEF -and $nameRaw[1] -eq 0xBB -and $nameRaw[2] -eq 0xBF) {
752
+ $nameRaw = $nameRaw[3..($nameRaw.Length-1)]
753
+ }
754
+ $name = [System.Text.Encoding]::UTF8.GetString($nameRaw).Trim()
665
755
 
666
756
  Write-Host ""
667
757
  Write-Host " Detected profile: $name" -ForegroundColor Cyan
@@ -694,12 +784,16 @@ function Apply-ImportToken($token) {
694
784
  Write-Host " Profile restored (credentials, settings, session)" -ForegroundColor Green
695
785
 
696
786
  $launcherSrc = "$extractDir\launcher.bat"
697
- $launcherDest = "$ACCOUNTS_DIR\$name.bat"
698
- if (Test-Path $launcherSrc) {
699
- if (!(Test-Path $ACCOUNTS_DIR)) { New-Item -ItemType Directory -Path $ACCOUNTS_DIR | Out-Null }
700
- Copy-Item $launcherSrc $launcherDest -Force
701
- Write-Host " Launcher created" -ForegroundColor Green
787
+ if (!(Test-Path $launcherSrc)) {
788
+ # Cross-platform: generate .bat from profile name if only .sh exists
789
+ $batContent = "@echo off`r`nset CLAUDE_CONFIG_DIR=%USERPROFILE%\.$name`r`nclaude %*"
790
+ $launcherSrc = "$extractDir\launcher.bat"
791
+ [System.IO.File]::WriteAllText($launcherSrc, $batContent)
702
792
  }
793
+ $launcherDest = "$ACCOUNTS_DIR\$name.bat"
794
+ if (!(Test-Path $ACCOUNTS_DIR)) { New-Item -ItemType Directory -Path $ACCOUNTS_DIR | Out-Null }
795
+ Copy-Item $launcherSrc $launcherDest -Force
796
+ Write-Host " Launcher created" -ForegroundColor Green
703
797
 
704
798
  Write-Host ""
705
799
  Write-Host " Profile '$name' imported successfully!" -ForegroundColor Green