@kaitranntt/ccs 2.4.0
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/LICENSE +21 -0
- package/README.md +287 -0
- package/README.vi.md +257 -0
- package/VERSION +1 -0
- package/bin/ccs.js +196 -0
- package/bin/claude-detector.js +156 -0
- package/bin/config-manager.js +129 -0
- package/bin/helpers.js +65 -0
- package/config/base-glm.settings.json +10 -0
- package/config/config.example.json +6 -0
- package/lib/ccs +414 -0
- package/lib/ccs.ps1 +493 -0
- package/package.json +52 -0
package/lib/ccs.ps1
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
# CCS - Claude Code Switch (Windows PowerShell)
|
|
2
|
+
# Cross-platform Claude CLI profile switcher
|
|
3
|
+
# https://github.com/kaitranntt/ccs
|
|
4
|
+
|
|
5
|
+
param(
|
|
6
|
+
[Parameter(Position=0)]
|
|
7
|
+
[string]$ProfileOrFlag = "default",
|
|
8
|
+
|
|
9
|
+
[Parameter(ValueFromRemainingArguments=$true)]
|
|
10
|
+
[string[]]$RemainingArgs
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
$ErrorActionPreference = "Stop"
|
|
14
|
+
|
|
15
|
+
# --- Color/Format Functions ---
|
|
16
|
+
function Write-ErrorMsg {
|
|
17
|
+
param([string]$Message)
|
|
18
|
+
Write-Host ""
|
|
19
|
+
Write-Host "╔═════════════════════════════════════════════╗" -ForegroundColor Red
|
|
20
|
+
Write-Host "║ ERROR ║" -ForegroundColor Red
|
|
21
|
+
Write-Host "╚═════════════════════════════════════════════╝" -ForegroundColor Red
|
|
22
|
+
Write-Host ""
|
|
23
|
+
Write-Host $Message -ForegroundColor Red
|
|
24
|
+
Write-Host ""
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# --- Claude CLI Detection Logic ---
|
|
28
|
+
|
|
29
|
+
function Find-ClaudeCli {
|
|
30
|
+
[OutputType([string])]
|
|
31
|
+
param()
|
|
32
|
+
|
|
33
|
+
# Priority 1: CCS_CLAUDE_PATH environment variable (if user wants custom path)
|
|
34
|
+
$CcsClaudePath = $env:CCS_CLAUDE_PATH
|
|
35
|
+
if ($CcsClaudePath) {
|
|
36
|
+
# Basic validation: file exists
|
|
37
|
+
if (Test-Path $CcsClaudePath -PathType Leaf) {
|
|
38
|
+
return $CcsClaudePath
|
|
39
|
+
}
|
|
40
|
+
# Invalid CCS_CLAUDE_PATH - show warning and fall back to PATH
|
|
41
|
+
Write-Host "[!] Warning: CCS_CLAUDE_PATH is set but file not found: $CcsClaudePath" -ForegroundColor Yellow
|
|
42
|
+
Write-Host " Falling back to system PATH lookup..." -ForegroundColor Yellow
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Priority 2: Use 'claude' from PATH (trust the system)
|
|
46
|
+
# This is the standard case - if user installed Claude CLI, it's in their PATH
|
|
47
|
+
return "claude"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function Show-ClaudeNotFoundError {
|
|
51
|
+
Write-ErrorMsg @"
|
|
52
|
+
Claude CLI not found in PATH
|
|
53
|
+
|
|
54
|
+
CCS requires Claude CLI to be installed and available in your PATH.
|
|
55
|
+
|
|
56
|
+
Solutions:
|
|
57
|
+
1. Install Claude CLI:
|
|
58
|
+
https://docs.claude.com/en/docs/claude-code/installation
|
|
59
|
+
|
|
60
|
+
2. Verify installation:
|
|
61
|
+
Get-Command claude
|
|
62
|
+
|
|
63
|
+
3. If installed but not in PATH, add it:
|
|
64
|
+
# Find Claude installation
|
|
65
|
+
where.exe claude
|
|
66
|
+
|
|
67
|
+
# Or set custom path
|
|
68
|
+
`$env:CCS_CLAUDE_PATH = 'C:\path\to\claude.exe'
|
|
69
|
+
|
|
70
|
+
Restart your terminal after installation.
|
|
71
|
+
"@
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Version (updated by scripts/bump-version.sh)
|
|
75
|
+
$CcsVersion = "2.4.0"
|
|
76
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
77
|
+
|
|
78
|
+
# Installation function for commands and skills
|
|
79
|
+
function Install-CommandsAndSkills {
|
|
80
|
+
# Try both possible locations for .claude directory
|
|
81
|
+
$SourceDir = $null
|
|
82
|
+
$PossibleDirs = @(
|
|
83
|
+
(Join-Path $ScriptDir ".claude"), # Development: tools/ccs/.claude
|
|
84
|
+
(Join-Path $env:USERPROFILE ".ccs\.claude") # Installed: ~/.ccs/.claude
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
foreach ($Dir in $PossibleDirs) {
|
|
88
|
+
if (Test-Path $Dir) {
|
|
89
|
+
$SourceDir = $Dir
|
|
90
|
+
break
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
$HomeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE }
|
|
95
|
+
$TargetDir = Join-Path $HomeDir ".claude"
|
|
96
|
+
|
|
97
|
+
Write-Host "[Installing CCS Commands and Skills]" -ForegroundColor Cyan
|
|
98
|
+
Write-Host "│ Source: $SourceDir"
|
|
99
|
+
Write-Host "│ Target: $TargetDir"
|
|
100
|
+
Write-Host "│"
|
|
101
|
+
|
|
102
|
+
# Check if source directory exists
|
|
103
|
+
if (-not $SourceDir) {
|
|
104
|
+
Write-Host "│"
|
|
105
|
+
$DevelopmentPath = Join-Path $ScriptDir ".claude"
|
|
106
|
+
$InstalledPath = Join-Path $env:USERPROFILE ".ccs\.claude"
|
|
107
|
+
Write-ErrorMsg @"
|
|
108
|
+
Source directory not found.
|
|
109
|
+
|
|
110
|
+
Checked locations:
|
|
111
|
+
- $DevelopmentPath (development)
|
|
112
|
+
- $InstalledPath (installed)
|
|
113
|
+
|
|
114
|
+
Solution:
|
|
115
|
+
1. If developing: Ensure you're in the CCS repository
|
|
116
|
+
2. If installed: Reinstall CCS with: irm ccs.kaitran.ca/install | iex
|
|
117
|
+
"@
|
|
118
|
+
exit 1
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Create target directories if they don't exist
|
|
122
|
+
$CommandsDir = Join-Path $TargetDir "commands"
|
|
123
|
+
$SkillsDir = Join-Path $TargetDir "skills"
|
|
124
|
+
|
|
125
|
+
if (-not (Test-Path $CommandsDir)) {
|
|
126
|
+
New-Item -ItemType Directory -Path $CommandsDir -Force | Out-Null
|
|
127
|
+
}
|
|
128
|
+
if (-not (Test-Path $SkillsDir)) {
|
|
129
|
+
New-Item -ItemType Directory -Path $SkillsDir -Force | Out-Null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
$InstalledCount = 0
|
|
133
|
+
$SkippedCount = 0
|
|
134
|
+
|
|
135
|
+
# Install commands
|
|
136
|
+
$SourceCommandsDir = Join-Path $SourceDir "commands"
|
|
137
|
+
if (Test-Path $SourceCommandsDir) {
|
|
138
|
+
Write-Host "│ Installing commands..." -ForegroundColor Yellow
|
|
139
|
+
Get-ChildItem $SourceCommandsDir -Filter "*.md" | ForEach-Object {
|
|
140
|
+
$CmdName = $_.BaseName
|
|
141
|
+
$TargetFile = Join-Path $CommandsDir "$CmdName.md"
|
|
142
|
+
|
|
143
|
+
if (Test-Path $TargetFile) {
|
|
144
|
+
Write-Host "│ | [i] Skipping existing command: $CmdName.md" -ForegroundColor Yellow
|
|
145
|
+
$SkippedCount++
|
|
146
|
+
} else {
|
|
147
|
+
try {
|
|
148
|
+
Copy-Item $_.FullName $TargetFile -ErrorAction Stop
|
|
149
|
+
Write-Host "│ | [OK] Installed command: $CmdName.md" -ForegroundColor Green
|
|
150
|
+
$InstalledCount++
|
|
151
|
+
} catch {
|
|
152
|
+
Write-Host "│ | [!] Failed to install command: $CmdName.md" -ForegroundColor Red
|
|
153
|
+
Write-Host "│ Error: $($_.Exception.Message)" -ForegroundColor Red
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
Write-Host "│ [i] No commands directory found" -ForegroundColor Gray
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
Write-Host "│"
|
|
162
|
+
|
|
163
|
+
# Install skills
|
|
164
|
+
$SourceSkillsDir = Join-Path $SourceDir "skills"
|
|
165
|
+
if (Test-Path $SourceSkillsDir) {
|
|
166
|
+
Write-Host "│ Installing skills..." -ForegroundColor Yellow
|
|
167
|
+
Get-ChildItem $SourceSkillsDir -Directory | ForEach-Object {
|
|
168
|
+
$SkillName = $_.Name
|
|
169
|
+
$TargetSkillDir = Join-Path $SkillsDir $SkillName
|
|
170
|
+
|
|
171
|
+
if (Test-Path $TargetSkillDir) {
|
|
172
|
+
Write-Host "│ | [i] Skipping existing skill: $SkillName" -ForegroundColor Yellow
|
|
173
|
+
$SkippedCount++
|
|
174
|
+
} else {
|
|
175
|
+
try {
|
|
176
|
+
Copy-Item $_.FullName $TargetSkillDir -Recurse -ErrorAction Stop
|
|
177
|
+
Write-Host "│ | [OK] Installed skill: $SkillName" -ForegroundColor Green
|
|
178
|
+
$InstalledCount++
|
|
179
|
+
} catch {
|
|
180
|
+
Write-Host "│ | [!] Failed to install skill: $SkillName" -ForegroundColor Red
|
|
181
|
+
Write-Host "│ Error: $($_.Exception.Message)" -ForegroundColor Red
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
Write-Host "│ [i] No skills directory found" -ForegroundColor Gray
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
Write-Host "[DONE]"
|
|
190
|
+
Write-Host ""
|
|
191
|
+
Write-Host "[OK] Installation complete!" -ForegroundColor Green
|
|
192
|
+
Write-Host " Installed: $InstalledCount items"
|
|
193
|
+
Write-Host " Skipped: $SkippedCount items (already exist)"
|
|
194
|
+
Write-Host ""
|
|
195
|
+
Write-Host "You can now use the /ccs command in Claude CLI for task delegation." -ForegroundColor Cyan
|
|
196
|
+
Write-Host "Example: /ccs glm /plan 'add user authentication'" -ForegroundColor Cyan
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Uninstallation function for commands and skills
|
|
200
|
+
function Uninstall-CommandsAndSkills {
|
|
201
|
+
$HomeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE }
|
|
202
|
+
$TargetDir = Join-Path $HomeDir ".claude"
|
|
203
|
+
$RemovedCount = 0
|
|
204
|
+
$NotFoundCount = 0
|
|
205
|
+
|
|
206
|
+
Write-Host "[Uninstalling CCS Commands and Skills]" -ForegroundColor Cyan
|
|
207
|
+
Write-Host "│ Target: $TargetDir"
|
|
208
|
+
Write-Host "│"
|
|
209
|
+
|
|
210
|
+
# Check if target directory exists
|
|
211
|
+
if (-not (Test-Path $TargetDir)) {
|
|
212
|
+
Write-Host "│"
|
|
213
|
+
Write-Host "│ [i] Claude directory not found: $TargetDir" -ForegroundColor Gray
|
|
214
|
+
Write-Host "│ Nothing to uninstall."
|
|
215
|
+
Write-Host "[DONE]"
|
|
216
|
+
Write-Host ""
|
|
217
|
+
Write-Host "[OK] Uninstall complete!" -ForegroundColor Green
|
|
218
|
+
Write-Host " Removed: 0 items (nothing was installed)"
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# Remove commands
|
|
223
|
+
$CommandsDir = Join-Path $TargetDir "commands"
|
|
224
|
+
if (Test-Path $CommandsDir) {
|
|
225
|
+
Write-Host "│ Removing commands..." -ForegroundColor Yellow
|
|
226
|
+
$CmdFile = Join-Path $CommandsDir "ccs.md"
|
|
227
|
+
if (Test-Path $CmdFile) {
|
|
228
|
+
try {
|
|
229
|
+
Remove-Item $CmdFile -Force -ErrorAction Stop
|
|
230
|
+
Write-Host "│ | [OK] Removed command: ccs.md" -ForegroundColor Green
|
|
231
|
+
$RemovedCount++
|
|
232
|
+
} catch {
|
|
233
|
+
Write-Host "│ | [!] Failed to remove command: ccs.md" -ForegroundColor Red
|
|
234
|
+
Write-Host "│ Error: $($_.Exception.Message)" -ForegroundColor Red
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
Write-Host "│ | [i] CCS command not found" -ForegroundColor Gray
|
|
238
|
+
$NotFoundCount++
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
Write-Host "│ [i] Commands directory not found" -ForegroundColor Gray
|
|
242
|
+
$NotFoundCount++
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Write-Host "│"
|
|
246
|
+
|
|
247
|
+
# Remove skills
|
|
248
|
+
$SkillsDir = Join-Path $TargetDir "skills"
|
|
249
|
+
if (Test-Path $SkillsDir) {
|
|
250
|
+
Write-Host "│ Removing skills..." -ForegroundColor Yellow
|
|
251
|
+
$SkillDir = Join-Path $SkillsDir "ccs-delegation"
|
|
252
|
+
if (Test-Path $SkillDir) {
|
|
253
|
+
try {
|
|
254
|
+
Remove-Item $SkillDir -Recurse -Force -ErrorAction Stop
|
|
255
|
+
Write-Host "│ | [OK] Removed skill: ccs-delegation" -ForegroundColor Green
|
|
256
|
+
$RemovedCount++
|
|
257
|
+
} catch {
|
|
258
|
+
Write-Host "│ | [!] Failed to remove skill: ccs-delegation" -ForegroundColor Red
|
|
259
|
+
Write-Host "│ Error: $($_.Exception.Message)" -ForegroundColor Red
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
Write-Host "│ | [i] CCS skill not found" -ForegroundColor Gray
|
|
263
|
+
$NotFoundCount++
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
Write-Host "│ [i] Skills directory not found" -ForegroundColor Gray
|
|
267
|
+
$NotFoundCount++
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
Write-Host "[DONE]"
|
|
271
|
+
Write-Host ""
|
|
272
|
+
Write-Host "[OK] Uninstall complete!" -ForegroundColor Green
|
|
273
|
+
Write-Host " Removed: $RemovedCount items"
|
|
274
|
+
Write-Host " Not found: $NotFoundCount items (already removed)"
|
|
275
|
+
Write-Host ""
|
|
276
|
+
Write-Host "The /ccs command is no longer available in Claude CLI." -ForegroundColor Cyan
|
|
277
|
+
Write-Host "To reinstall: ccs --install" -ForegroundColor Cyan
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
# Special case: version command (check BEFORE profile detection)
|
|
281
|
+
# Check both $ProfileOrFlag and first element of $RemainingArgs
|
|
282
|
+
$FirstArg = if ($ProfileOrFlag -ne "default") { $ProfileOrFlag } elseif ($RemainingArgs.Count -gt 0) { $RemainingArgs[0] } else { $null }
|
|
283
|
+
if ($FirstArg -eq "version" -or $FirstArg -eq "--version" -or $FirstArg -eq "-v") {
|
|
284
|
+
Write-Host "CCS (Claude Code Switch) version $CcsVersion"
|
|
285
|
+
|
|
286
|
+
# Show install location
|
|
287
|
+
$InstallLocation = (Get-Command ccs -ErrorAction SilentlyContinue).Source
|
|
288
|
+
if ($InstallLocation) {
|
|
289
|
+
Write-Host "Installed at: $InstallLocation"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
Write-Host "https://github.com/kaitranntt/ccs"
|
|
293
|
+
exit 0
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# Special case: help command (check BEFORE profile detection)
|
|
297
|
+
if ($FirstArg -eq "--help" -or $FirstArg -eq "-h" -or $FirstArg -eq "help") {
|
|
298
|
+
$ClaudeCli = Find-ClaudeCli
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
if ($RemainingArgs) {
|
|
302
|
+
& $ClaudeCli --help @RemainingArgs
|
|
303
|
+
} else {
|
|
304
|
+
& $ClaudeCli --help
|
|
305
|
+
}
|
|
306
|
+
exit $LASTEXITCODE
|
|
307
|
+
} catch {
|
|
308
|
+
Show-ClaudeNotFoundError
|
|
309
|
+
exit 1
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
# Special case: install command (check BEFORE profile detection)
|
|
314
|
+
if ($FirstArg -eq "--install") {
|
|
315
|
+
Install-CommandsAndSkills
|
|
316
|
+
exit $LASTEXITCODE
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# Special case: uninstall command (check BEFORE profile detection)
|
|
320
|
+
if ($FirstArg -eq "--uninstall") {
|
|
321
|
+
Uninstall-CommandsAndSkills
|
|
322
|
+
exit $LASTEXITCODE
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Smart profile detection: if first arg starts with '-', it's a flag not a profile
|
|
326
|
+
if ($ProfileOrFlag -match '^-') {
|
|
327
|
+
# First arg is a flag → use default profile, keep all args
|
|
328
|
+
$Profile = "default"
|
|
329
|
+
# Prepend $ProfileOrFlag to $RemainingArgs (it's actually a flag, not a profile)
|
|
330
|
+
if ($RemainingArgs) {
|
|
331
|
+
$RemainingArgs = @($ProfileOrFlag) + $RemainingArgs
|
|
332
|
+
} else {
|
|
333
|
+
$RemainingArgs = @($ProfileOrFlag)
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
# First arg is a profile name
|
|
337
|
+
$Profile = $ProfileOrFlag
|
|
338
|
+
# $RemainingArgs already contains correct args (PowerShell handles this)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
# Special case: "default" profile just runs claude directly (no profile switching)
|
|
342
|
+
if ($Profile -eq "default") {
|
|
343
|
+
try {
|
|
344
|
+
if ($RemainingArgs) {
|
|
345
|
+
& claude @RemainingArgs
|
|
346
|
+
} else {
|
|
347
|
+
& claude
|
|
348
|
+
}
|
|
349
|
+
exit $LASTEXITCODE
|
|
350
|
+
} catch {
|
|
351
|
+
Write-Host "Error: Failed to execute claude" -ForegroundColor Red
|
|
352
|
+
Write-Host $_.Exception.Message
|
|
353
|
+
exit 1
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
# Config file location (supports environment variable override)
|
|
358
|
+
$ConfigFile = if ($env:CCS_CONFIG) {
|
|
359
|
+
$env:CCS_CONFIG
|
|
360
|
+
} else {
|
|
361
|
+
"$env:USERPROFILE\.ccs\config.json"
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
# Check config exists
|
|
365
|
+
if (-not (Test-Path $ConfigFile)) {
|
|
366
|
+
Write-ErrorMsg @"
|
|
367
|
+
Config file not found: $ConfigFile
|
|
368
|
+
|
|
369
|
+
Solutions:
|
|
370
|
+
1. Reinstall CCS:
|
|
371
|
+
irm ccs.kaitran.ca/install | iex
|
|
372
|
+
|
|
373
|
+
2. Or create config manually:
|
|
374
|
+
New-Item -ItemType Directory -Force -Path '$env:USERPROFILE\.ccs'
|
|
375
|
+
Set-Content -Path '$env:USERPROFILE\.ccs\config.json' -Value '{
|
|
376
|
+
"profiles": {
|
|
377
|
+
"glm": "~/.ccs/glm.settings.json",
|
|
378
|
+
"default": "~/.claude/settings.json"
|
|
379
|
+
}
|
|
380
|
+
}'
|
|
381
|
+
"@
|
|
382
|
+
exit 1
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
# Validate profile name (alphanumeric, dash, underscore only)
|
|
386
|
+
if ($Profile -notmatch '^[a-zA-Z0-9_-]+$') {
|
|
387
|
+
Write-ErrorMsg @"
|
|
388
|
+
Invalid profile name: $Profile
|
|
389
|
+
|
|
390
|
+
Use only alphanumeric characters, dash, or underscore.
|
|
391
|
+
"@
|
|
392
|
+
exit 1
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# Read and parse JSON config
|
|
396
|
+
try {
|
|
397
|
+
$ConfigContent = Get-Content $ConfigFile -Raw -ErrorAction Stop
|
|
398
|
+
$Config = $ConfigContent | ConvertFrom-Json -ErrorAction Stop
|
|
399
|
+
} catch {
|
|
400
|
+
Write-ErrorMsg @"
|
|
401
|
+
Invalid JSON in $ConfigFile
|
|
402
|
+
|
|
403
|
+
Fix the JSON syntax or reinstall:
|
|
404
|
+
irm ccs.kaitran.ca/install | iex
|
|
405
|
+
"@
|
|
406
|
+
exit 1
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
# Validate config has profiles object
|
|
410
|
+
if (-not $Config.profiles) {
|
|
411
|
+
Write-ErrorMsg @"
|
|
412
|
+
Config must have 'profiles' object
|
|
413
|
+
|
|
414
|
+
See .ccs.example.json for correct format
|
|
415
|
+
Or reinstall:
|
|
416
|
+
irm ccs.kaitran.ca/install | iex
|
|
417
|
+
"@
|
|
418
|
+
exit 1
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
# Get settings path for profile
|
|
422
|
+
$SettingsPath = $Config.profiles.$Profile
|
|
423
|
+
|
|
424
|
+
if (-not $SettingsPath) {
|
|
425
|
+
$AvailableProfiles = ($Config.profiles.PSObject.Properties.Name | ForEach-Object { " - $_" }) -join "`n"
|
|
426
|
+
Write-ErrorMsg @"
|
|
427
|
+
Profile '$Profile' not found in $ConfigFile
|
|
428
|
+
|
|
429
|
+
Available profiles:
|
|
430
|
+
$AvailableProfiles
|
|
431
|
+
"@
|
|
432
|
+
exit 1
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# Path expansion and normalization
|
|
436
|
+
# 1. Handle Unix-style tilde expansion (~/path -> %USERPROFILE%\path)
|
|
437
|
+
if ($SettingsPath -match '^~[/\\]') {
|
|
438
|
+
$SettingsPath = $SettingsPath -replace '^~', $env:USERPROFILE
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# 2. Expand Windows environment variables (%USERPROFILE%, etc.)
|
|
442
|
+
$SettingsPath = [System.Environment]::ExpandEnvironmentVariables($SettingsPath)
|
|
443
|
+
|
|
444
|
+
# 3. Convert forward slashes to backslashes (Unix path compatibility)
|
|
445
|
+
$SettingsPath = $SettingsPath -replace '/', '\'
|
|
446
|
+
|
|
447
|
+
# Validate settings file exists
|
|
448
|
+
if (-not (Test-Path $SettingsPath)) {
|
|
449
|
+
Write-ErrorMsg @"
|
|
450
|
+
Settings file not found: $SettingsPath
|
|
451
|
+
|
|
452
|
+
Solutions:
|
|
453
|
+
1. Create the settings file for profile '$Profile'
|
|
454
|
+
2. Update the path in $ConfigFile
|
|
455
|
+
3. Or reinstall: irm ccs.kaitran.ca/install | iex
|
|
456
|
+
"@
|
|
457
|
+
exit 1
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
# Validate settings file is valid JSON (basic check)
|
|
461
|
+
try {
|
|
462
|
+
$SettingsContent = Get-Content $SettingsPath -Raw -ErrorAction Stop
|
|
463
|
+
$Settings = $SettingsContent | ConvertFrom-Json -ErrorAction Stop
|
|
464
|
+
} catch {
|
|
465
|
+
Write-ErrorMsg @"
|
|
466
|
+
Invalid JSON in $SettingsPath
|
|
467
|
+
|
|
468
|
+
Details: $_
|
|
469
|
+
|
|
470
|
+
Solutions:
|
|
471
|
+
1. Validate JSON at https://jsonlint.com
|
|
472
|
+
2. Or reset to template:
|
|
473
|
+
Set-Content -Path '$SettingsPath' -Value '{`"env`":{}}`'
|
|
474
|
+
3. Or reinstall: irm ccs.kaitran.ca/install | iex
|
|
475
|
+
"@
|
|
476
|
+
exit 1
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
# Detect Claude CLI executable
|
|
480
|
+
$ClaudeCli = Find-ClaudeCli
|
|
481
|
+
|
|
482
|
+
# Execute Claude with the profile settings
|
|
483
|
+
try {
|
|
484
|
+
if ($RemainingArgs) {
|
|
485
|
+
& $ClaudeCli --settings $SettingsPath @RemainingArgs
|
|
486
|
+
} else {
|
|
487
|
+
& $ClaudeCli --settings $SettingsPath
|
|
488
|
+
}
|
|
489
|
+
exit $LASTEXITCODE
|
|
490
|
+
} catch {
|
|
491
|
+
Show-ClaudeNotFoundError
|
|
492
|
+
exit 1
|
|
493
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kaitranntt/ccs",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"claude",
|
|
8
|
+
"glm",
|
|
9
|
+
"ai",
|
|
10
|
+
"profile",
|
|
11
|
+
"switch"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/kaitranntt/ccs",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/kaitranntt/ccs/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/kaitranntt/ccs.git"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": {
|
|
23
|
+
"name": "Tam Nhu Tran (Kai)",
|
|
24
|
+
"email": "kaitranntt@users.noreply.github.com"
|
|
25
|
+
},
|
|
26
|
+
"bin": {
|
|
27
|
+
"ccs": "bin/ccs.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin/",
|
|
31
|
+
"lib/",
|
|
32
|
+
"config/",
|
|
33
|
+
"VERSION",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=14.0.0"
|
|
39
|
+
},
|
|
40
|
+
"os": [
|
|
41
|
+
"darwin",
|
|
42
|
+
"linux",
|
|
43
|
+
"win32"
|
|
44
|
+
],
|
|
45
|
+
"preferGlobal": true,
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "bash tests/edge-cases.sh",
|
|
48
|
+
"prepublishOnly": "node scripts/sync-version.js",
|
|
49
|
+
"prepack": "node scripts/sync-version.js",
|
|
50
|
+
"prepare": "node scripts/check-executables.js"
|
|
51
|
+
}
|
|
52
|
+
}
|