@kaitranntt/ccs 3.4.6 → 4.1.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/.claude/agents/ccs-delegator.md +117 -0
- package/.claude/commands/ccs/glm/continue.md +22 -0
- package/.claude/commands/ccs/glm.md +22 -0
- package/.claude/commands/ccs/kimi/continue.md +22 -0
- package/.claude/commands/ccs/kimi.md +22 -0
- package/.claude/skills/ccs-delegation/SKILL.md +54 -0
- package/.claude/skills/ccs-delegation/references/README.md +24 -0
- package/.claude/skills/ccs-delegation/references/delegation-guidelines.md +99 -0
- package/.claude/skills/ccs-delegation/references/headless-workflow.md +174 -0
- package/.claude/skills/ccs-delegation/references/troubleshooting.md +268 -0
- package/README.ja.md +470 -146
- package/README.md +532 -145
- package/README.vi.md +484 -157
- package/VERSION +1 -1
- package/bin/auth/auth-commands.js +98 -13
- package/bin/auth/profile-detector.js +11 -6
- package/bin/ccs.js +148 -2
- package/bin/delegation/README.md +189 -0
- package/bin/delegation/delegation-handler.js +212 -0
- package/bin/delegation/headless-executor.js +617 -0
- package/bin/delegation/result-formatter.js +483 -0
- package/bin/delegation/session-manager.js +156 -0
- package/bin/delegation/settings-parser.js +109 -0
- package/bin/management/doctor.js +94 -1
- package/bin/utils/claude-symlink-manager.js +238 -0
- package/bin/utils/delegation-validator.js +154 -0
- package/bin/utils/error-codes.js +59 -0
- package/bin/utils/error-manager.js +38 -32
- package/bin/utils/helpers.js +65 -1
- package/bin/utils/progress-indicator.js +111 -0
- package/bin/utils/prompt.js +134 -0
- package/bin/utils/shell-completion.js +234 -0
- package/lib/ccs +575 -25
- package/lib/ccs.ps1 +381 -20
- package/lib/error-codes.ps1 +55 -0
- package/lib/error-codes.sh +63 -0
- package/lib/progress-indicator.ps1 +120 -0
- package/lib/progress-indicator.sh +117 -0
- package/lib/prompt.ps1 +109 -0
- package/lib/prompt.sh +99 -0
- package/package.json +2 -1
- package/scripts/completion/README.md +308 -0
- package/scripts/completion/ccs.bash +81 -0
- package/scripts/completion/ccs.fish +92 -0
- package/scripts/completion/ccs.ps1 +157 -0
- package/scripts/completion/ccs.zsh +130 -0
- package/scripts/postinstall.js +35 -0
package/lib/ccs.ps1
CHANGED
|
@@ -12,12 +12,32 @@ param(
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
13
|
|
|
14
14
|
# Version (updated by scripts/bump-version.sh)
|
|
15
|
-
$CcsVersion = "
|
|
15
|
+
$CcsVersion = "4.1.0"
|
|
16
16
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
17
17
|
$ConfigFile = if ($env:CCS_CONFIG) { $env:CCS_CONFIG } else { "$env:USERPROFILE\.ccs\config.json" }
|
|
18
18
|
$ProfilesJson = "$env:USERPROFILE\.ccs\profiles.json"
|
|
19
19
|
$InstancesDir = "$env:USERPROFILE\.ccs\instances"
|
|
20
20
|
|
|
21
|
+
# Determine dependency location (git vs installed)
|
|
22
|
+
# Git: lib/ccs.ps1 and dependencies are in same dir (lib/)
|
|
23
|
+
# Installed: lib/ccs.ps1 is in ~/.ccs/, dependencies in ~/.ccs/lib/
|
|
24
|
+
$DepDir = if (Test-Path "$ScriptDir\error-codes.ps1") {
|
|
25
|
+
# Git install - files in same directory
|
|
26
|
+
$ScriptDir
|
|
27
|
+
} else {
|
|
28
|
+
# Standalone install - files in ~/.ccs/lib/
|
|
29
|
+
"$env:USERPROFILE\.ccs\lib"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Source error codes
|
|
33
|
+
. "$DepDir\error-codes.ps1"
|
|
34
|
+
|
|
35
|
+
# Source progress indicators
|
|
36
|
+
. "$DepDir\progress-indicator.ps1"
|
|
37
|
+
|
|
38
|
+
# Source interactive prompts
|
|
39
|
+
. "$DepDir\prompt.ps1"
|
|
40
|
+
|
|
21
41
|
# --- Color/Format Functions ---
|
|
22
42
|
function Write-ErrorMsg {
|
|
23
43
|
param([string]$Message)
|
|
@@ -30,6 +50,104 @@ function Write-ErrorMsg {
|
|
|
30
50
|
Write-Host ""
|
|
31
51
|
}
|
|
32
52
|
|
|
53
|
+
# Calculate Levenshtein distance between two strings
|
|
54
|
+
function Get-LevenshteinDistance {
|
|
55
|
+
param(
|
|
56
|
+
[string]$a,
|
|
57
|
+
[string]$b
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
$lenA = $a.Length
|
|
61
|
+
$lenB = $b.Length
|
|
62
|
+
|
|
63
|
+
if ($lenA -eq 0) { return $lenB }
|
|
64
|
+
if ($lenB -eq 0) { return $lenA }
|
|
65
|
+
|
|
66
|
+
# Initialize matrix
|
|
67
|
+
$matrix = New-Object 'int[,]' ($lenB + 1), ($lenA + 1)
|
|
68
|
+
|
|
69
|
+
# Initialize first row and column
|
|
70
|
+
for ($i = 0; $i -le $lenB; $i++) {
|
|
71
|
+
$matrix[$i, 0] = $i
|
|
72
|
+
}
|
|
73
|
+
for ($j = 0; $j -le $lenA; $j++) {
|
|
74
|
+
$matrix[0, $j] = $j
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Fill matrix
|
|
78
|
+
for ($i = 1; $i -le $lenB; $i++) {
|
|
79
|
+
for ($j = 1; $j -le $lenA; $j++) {
|
|
80
|
+
if ($a[$j - 1] -eq $b[$i - 1]) {
|
|
81
|
+
$matrix[$i, $j] = $matrix[$i - 1, $j - 1]
|
|
82
|
+
} else {
|
|
83
|
+
$sub = $matrix[$i - 1, $j - 1]
|
|
84
|
+
$ins = $matrix[$i, $j - 1]
|
|
85
|
+
$del = $matrix[$i - 1, $j]
|
|
86
|
+
$min = [Math]::Min([Math]::Min($sub, $ins), $del)
|
|
87
|
+
$matrix[$i, $j] = $min + 1
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return $matrix[$lenB, $lenA]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Find similar strings using fuzzy matching
|
|
96
|
+
function Find-SimilarStrings {
|
|
97
|
+
param(
|
|
98
|
+
[string]$Target,
|
|
99
|
+
[string[]]$Candidates,
|
|
100
|
+
[int]$MaxDistance = 2
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
$targetLower = $Target.ToLower()
|
|
104
|
+
$matches = @()
|
|
105
|
+
|
|
106
|
+
foreach ($candidate in $Candidates) {
|
|
107
|
+
$candidateLower = $candidate.ToLower()
|
|
108
|
+
$distance = Get-LevenshteinDistance $targetLower $candidateLower
|
|
109
|
+
|
|
110
|
+
if ($distance -le $MaxDistance -and $distance -gt 0) {
|
|
111
|
+
$matches += [PSCustomObject]@{
|
|
112
|
+
Name = $candidate
|
|
113
|
+
Distance = $distance
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Sort by distance and return top 3
|
|
119
|
+
return $matches | Sort-Object Distance | Select-Object -First 3 | ForEach-Object { $_.Name }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Enhanced error message with error codes
|
|
123
|
+
function Show-EnhancedError {
|
|
124
|
+
param(
|
|
125
|
+
[string]$ErrorCode,
|
|
126
|
+
[string]$ShortMsg,
|
|
127
|
+
[string]$Context = "",
|
|
128
|
+
[string]$Suggestions = ""
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
Write-Host ""
|
|
132
|
+
Write-Host "[X] $ShortMsg" -ForegroundColor Red
|
|
133
|
+
Write-Host ""
|
|
134
|
+
|
|
135
|
+
if ($Context) {
|
|
136
|
+
Write-Host $Context
|
|
137
|
+
Write-Host ""
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if ($Suggestions) {
|
|
141
|
+
Write-Host "Solutions:" -ForegroundColor Yellow
|
|
142
|
+
Write-Host $Suggestions
|
|
143
|
+
Write-Host ""
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
Write-Host "Error: $ErrorCode" -ForegroundColor Yellow
|
|
147
|
+
Write-Host (Get-ErrorDocUrl $ErrorCode) -ForegroundColor Yellow
|
|
148
|
+
Write-Host ""
|
|
149
|
+
}
|
|
150
|
+
|
|
33
151
|
function Write-ColoredText {
|
|
34
152
|
param(
|
|
35
153
|
[string]$Text,
|
|
@@ -111,6 +229,19 @@ function Show-Help {
|
|
|
111
229
|
Write-ColorLine " ccs kimi Switch to Kimi for Coding" "Yellow"
|
|
112
230
|
Write-ColorLine " ccs glm 'debug this code' Use GLM and run command" "Yellow"
|
|
113
231
|
Write-Host ""
|
|
232
|
+
Write-ColorLine "Examples:" "Cyan"
|
|
233
|
+
Write-Host " Quick start:"
|
|
234
|
+
Write-ColorLine " `$ ccs" "Yellow" -NoNewline
|
|
235
|
+
Write-Host " # Use default account"
|
|
236
|
+
Write-ColorLine " `$ ccs glm `"implement API`"" "Yellow" -NoNewline
|
|
237
|
+
Write-Host " # Cost-optimized model"
|
|
238
|
+
Write-Host ""
|
|
239
|
+
Write-Host " Profile usage:"
|
|
240
|
+
Write-ColorLine " `$ ccs work `"debug code`"" "Yellow" -NoNewline
|
|
241
|
+
Write-Host " # Switch to work profile"
|
|
242
|
+
Write-ColorLine " `$ ccs personal" "Yellow" -NoNewline
|
|
243
|
+
Write-Host " # Open personal account"
|
|
244
|
+
Write-Host ""
|
|
114
245
|
Write-ColorLine "Account Management:" "Cyan"
|
|
115
246
|
Write-ColorLine " ccs auth --help Manage multiple Claude accounts" "Yellow"
|
|
116
247
|
Write-ColorLine " ccs work Switch to work account" "Yellow"
|
|
@@ -122,6 +253,7 @@ function Show-Help {
|
|
|
122
253
|
Write-ColorLine "Flags:" "Cyan"
|
|
123
254
|
Write-ColorLine " -h, --help Show this help message" "Yellow"
|
|
124
255
|
Write-ColorLine " -v, --version Show version and installation info" "Yellow"
|
|
256
|
+
Write-ColorLine " --shell-completion Install shell auto-completion" "Yellow"
|
|
125
257
|
Write-Host ""
|
|
126
258
|
Write-ColorLine "Configuration:" "Cyan"
|
|
127
259
|
Write-Host " Config: ~/.ccs/config.json"
|
|
@@ -585,6 +717,28 @@ function Ensure-Instance {
|
|
|
585
717
|
|
|
586
718
|
# --- Profile Detection Logic (Phase 1) ---
|
|
587
719
|
|
|
720
|
+
function Get-AllProfileNames {
|
|
721
|
+
$names = @()
|
|
722
|
+
|
|
723
|
+
# Settings-based profiles
|
|
724
|
+
if (Test-Path $ConfigFile) {
|
|
725
|
+
try {
|
|
726
|
+
$Config = Get-Content $ConfigFile -Raw | ConvertFrom-Json
|
|
727
|
+
$names += $Config.profiles.PSObject.Properties.Name
|
|
728
|
+
} catch {}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
# Account-based profiles
|
|
732
|
+
if (Test-Path $ProfilesJson) {
|
|
733
|
+
try {
|
|
734
|
+
$Profiles = Read-ProfilesJson
|
|
735
|
+
$names += $Profiles.profiles.PSObject.Properties.Name
|
|
736
|
+
} catch {}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return $names
|
|
740
|
+
}
|
|
741
|
+
|
|
588
742
|
function Get-AvailableProfiles {
|
|
589
743
|
$lines = @()
|
|
590
744
|
|
|
@@ -718,6 +872,12 @@ function Show-AuthHelp {
|
|
|
718
872
|
Write-Host ' ccs work "review code" # Use work profile' -ForegroundColor Yellow
|
|
719
873
|
Write-Host ' ccs "review code" # Use default profile' -ForegroundColor Yellow
|
|
720
874
|
Write-Host ""
|
|
875
|
+
Write-Host "Options:" -ForegroundColor Cyan
|
|
876
|
+
Write-Host " --force Allow overwriting existing profile (create)" -ForegroundColor Yellow
|
|
877
|
+
Write-Host " --yes, -y Skip confirmation prompts (remove)" -ForegroundColor Yellow
|
|
878
|
+
Write-Host " --json Output in JSON format (list, show)" -ForegroundColor Yellow
|
|
879
|
+
Write-Host " --verbose Show additional details (list)" -ForegroundColor Yellow
|
|
880
|
+
Write-Host ""
|
|
721
881
|
Write-Host "Note:" -ForegroundColor Cyan
|
|
722
882
|
Write-Host " By default, " -NoNewline
|
|
723
883
|
Write-Host "ccs" -ForegroundColor Yellow -NoNewline
|
|
@@ -796,8 +956,13 @@ function Invoke-AuthList {
|
|
|
796
956
|
param([string[]]$Args)
|
|
797
957
|
|
|
798
958
|
$Verbose = $Args -contains "--verbose"
|
|
959
|
+
$Json = $Args -contains "--json"
|
|
799
960
|
|
|
800
961
|
if (-not (Test-Path $ProfilesJson)) {
|
|
962
|
+
if ($Json) {
|
|
963
|
+
Write-Output "{`"version`":`"$CcsVersion`",`"profiles`":[]}"
|
|
964
|
+
return
|
|
965
|
+
}
|
|
801
966
|
Write-Host "No account profiles found" -ForegroundColor Yellow
|
|
802
967
|
Write-Host ""
|
|
803
968
|
Write-Host "To create your first profile:"
|
|
@@ -809,10 +974,45 @@ function Invoke-AuthList {
|
|
|
809
974
|
$Profiles = $Data.profiles.PSObject.Properties.Name
|
|
810
975
|
|
|
811
976
|
if ($Profiles.Count -eq 0) {
|
|
977
|
+
if ($Json) {
|
|
978
|
+
Write-Output "{`"version`":`"$CcsVersion`",`"profiles`":[]}"
|
|
979
|
+
return
|
|
980
|
+
}
|
|
812
981
|
Write-Host "No account profiles found" -ForegroundColor Yellow
|
|
813
982
|
return
|
|
814
983
|
}
|
|
815
984
|
|
|
985
|
+
# JSON output mode
|
|
986
|
+
if ($Json) {
|
|
987
|
+
$ProfilesList = @()
|
|
988
|
+
foreach ($profile in $Profiles) {
|
|
989
|
+
$IsDefault = $profile -eq $Data.default
|
|
990
|
+
$Type = $Data.profiles.$profile.type
|
|
991
|
+
if (-not $Type) { $Type = "account" }
|
|
992
|
+
$Created = $Data.profiles.$profile.created
|
|
993
|
+
$LastUsed = $Data.profiles.$profile.last_used
|
|
994
|
+
$InstancePath = "$InstancesDir\$(Get-SanitizedProfileName $profile)"
|
|
995
|
+
|
|
996
|
+
$ProfilesList += @{
|
|
997
|
+
name = $profile
|
|
998
|
+
type = $Type
|
|
999
|
+
is_default = $IsDefault
|
|
1000
|
+
created = $Created
|
|
1001
|
+
last_used = $LastUsed
|
|
1002
|
+
instance_path = $InstancePath
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
$Output = @{
|
|
1007
|
+
version = $CcsVersion
|
|
1008
|
+
profiles = $ProfilesList
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
Write-Output ($Output | ConvertTo-Json -Depth 10)
|
|
1012
|
+
return
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
# Human-readable output
|
|
816
1016
|
Write-Host "Saved Account Profiles:" -ForegroundColor White
|
|
817
1017
|
Write-Host ""
|
|
818
1018
|
|
|
@@ -846,10 +1046,26 @@ function Invoke-AuthList {
|
|
|
846
1046
|
function Invoke-AuthShow {
|
|
847
1047
|
param([string[]]$Args)
|
|
848
1048
|
|
|
849
|
-
$ProfileName =
|
|
1049
|
+
$ProfileName = ""
|
|
1050
|
+
$Json = $false
|
|
1051
|
+
|
|
1052
|
+
# Parse arguments
|
|
1053
|
+
foreach ($arg in $Args) {
|
|
1054
|
+
if ($arg -eq "--json") {
|
|
1055
|
+
$Json = $true
|
|
1056
|
+
} elseif ($arg -like "-*") {
|
|
1057
|
+
Write-ErrorMsg "Unknown option: $arg"
|
|
1058
|
+
return 1
|
|
1059
|
+
} else {
|
|
1060
|
+
$ProfileName = $arg
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
850
1063
|
|
|
851
1064
|
if (-not $ProfileName) {
|
|
852
|
-
Write-ErrorMsg "Profile name is required
|
|
1065
|
+
Write-ErrorMsg "Profile name is required"
|
|
1066
|
+
Write-Host ""
|
|
1067
|
+
Write-Host "Usage: " -NoNewline
|
|
1068
|
+
Write-Host "ccs auth show <profile> [--json]" -ForegroundColor Yellow
|
|
853
1069
|
return 1
|
|
854
1070
|
}
|
|
855
1071
|
|
|
@@ -861,20 +1077,48 @@ function Invoke-AuthShow {
|
|
|
861
1077
|
$Data = Read-ProfilesJson
|
|
862
1078
|
$IsDefault = $ProfileName -eq $Data.default
|
|
863
1079
|
|
|
864
|
-
Write-Host "Profile: $ProfileName" -ForegroundColor White
|
|
865
|
-
Write-Host ""
|
|
866
|
-
|
|
867
1080
|
$Type = $Data.profiles.$ProfileName.type
|
|
1081
|
+
if (-not $Type) { $Type = "account" }
|
|
868
1082
|
$Created = $Data.profiles.$ProfileName.created
|
|
869
1083
|
$LastUsed = $Data.profiles.$ProfileName.last_used
|
|
870
|
-
if (-not $LastUsed) { $LastUsed = "Never" }
|
|
871
1084
|
$InstancePath = "$InstancesDir\$(Get-SanitizedProfileName $ProfileName)"
|
|
872
1085
|
|
|
1086
|
+
# Count sessions
|
|
1087
|
+
$SessionCount = 0
|
|
1088
|
+
if (Test-Path "$InstancePath\session-env") {
|
|
1089
|
+
$SessionFiles = Get-ChildItem "$InstancePath\session-env" -Filter "*.json" -ErrorAction SilentlyContinue
|
|
1090
|
+
$SessionCount = $SessionFiles.Count
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
# JSON output mode
|
|
1094
|
+
if ($Json) {
|
|
1095
|
+
$Output = @{
|
|
1096
|
+
name = $ProfileName
|
|
1097
|
+
type = $Type
|
|
1098
|
+
is_default = $IsDefault
|
|
1099
|
+
created = $Created
|
|
1100
|
+
last_used = $LastUsed
|
|
1101
|
+
instance_path = $InstancePath
|
|
1102
|
+
session_count = $SessionCount
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
Write-Output ($Output | ConvertTo-Json -Depth 10)
|
|
1106
|
+
return
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
# Human-readable output
|
|
1110
|
+
Write-Host "Profile: $ProfileName" -ForegroundColor White
|
|
1111
|
+
Write-Host ""
|
|
1112
|
+
|
|
873
1113
|
Write-Host " Type: $Type"
|
|
874
1114
|
Write-Host " Default: $(if ($IsDefault) { 'Yes' } else { 'No' })"
|
|
875
1115
|
Write-Host " Instance: $InstancePath"
|
|
876
1116
|
Write-Host " Created: $Created"
|
|
877
|
-
|
|
1117
|
+
if ($LastUsed) {
|
|
1118
|
+
Write-Host " Last used: $LastUsed"
|
|
1119
|
+
} else {
|
|
1120
|
+
Write-Host " Last used: Never"
|
|
1121
|
+
}
|
|
878
1122
|
Write-Host ""
|
|
879
1123
|
}
|
|
880
1124
|
|
|
@@ -882,18 +1126,24 @@ function Invoke-AuthRemove {
|
|
|
882
1126
|
param([string[]]$Args)
|
|
883
1127
|
|
|
884
1128
|
$ProfileName = ""
|
|
885
|
-
$Force = $false
|
|
886
1129
|
|
|
1130
|
+
# Parse arguments
|
|
887
1131
|
foreach ($arg in $Args) {
|
|
888
|
-
if ($arg -eq "--
|
|
889
|
-
$
|
|
1132
|
+
if ($arg -eq "--yes" -or $arg -eq "-y") {
|
|
1133
|
+
$env:CCS_YES = "1" # Auto-confirm
|
|
1134
|
+
} elseif ($arg -like "-*") {
|
|
1135
|
+
Write-ErrorMsg "Unknown option: $arg"
|
|
1136
|
+
return 1
|
|
890
1137
|
} else {
|
|
891
1138
|
$ProfileName = $arg
|
|
892
1139
|
}
|
|
893
1140
|
}
|
|
894
1141
|
|
|
895
1142
|
if (-not $ProfileName) {
|
|
896
|
-
Write-ErrorMsg "Profile name is required
|
|
1143
|
+
Write-ErrorMsg "Profile name is required"
|
|
1144
|
+
Write-Host ""
|
|
1145
|
+
Write-Host "Usage: " -NoNewline
|
|
1146
|
+
Write-Host "ccs auth remove <profile> [--yes]" -ForegroundColor Yellow
|
|
897
1147
|
return 1
|
|
898
1148
|
}
|
|
899
1149
|
|
|
@@ -902,13 +1152,33 @@ function Invoke-AuthRemove {
|
|
|
902
1152
|
return 1
|
|
903
1153
|
}
|
|
904
1154
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1155
|
+
# Get instance path and session count for impact display
|
|
1156
|
+
$InstancePath = "$InstancesDir\$(Get-SanitizedProfileName $ProfileName)"
|
|
1157
|
+
$SessionCount = 0
|
|
1158
|
+
|
|
1159
|
+
if (Test-Path "$InstancePath\session-env") {
|
|
1160
|
+
$SessionFiles = Get-ChildItem "$InstancePath\session-env" -Filter "*.json" -ErrorAction SilentlyContinue
|
|
1161
|
+
$SessionCount = $SessionFiles.Count
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
# Display impact
|
|
1165
|
+
Write-Host ""
|
|
1166
|
+
Write-Host "Profile '" -NoNewline
|
|
1167
|
+
Write-Host $ProfileName -ForegroundColor Cyan -NoNewline
|
|
1168
|
+
Write-Host "' will be permanently deleted."
|
|
1169
|
+
Write-Host " Instance path: $InstancePath"
|
|
1170
|
+
Write-Host " Sessions: $SessionCount conversation$(if ($SessionCount -ne 1) { 's' } else { '' })"
|
|
1171
|
+
Write-Host ""
|
|
1172
|
+
|
|
1173
|
+
# Interactive confirmation (or --yes flag)
|
|
1174
|
+
$Confirmed = Confirm-Action "Delete this profile?" "No"
|
|
1175
|
+
|
|
1176
|
+
if (-not $Confirmed) {
|
|
1177
|
+
Write-Host "[i] Cancelled"
|
|
1178
|
+
return 0
|
|
908
1179
|
}
|
|
909
1180
|
|
|
910
1181
|
# Delete instance directory
|
|
911
|
-
$InstancePath = "$InstancesDir\$(Get-SanitizedProfileName $ProfileName)"
|
|
912
1182
|
if (Test-Path $InstancePath) {
|
|
913
1183
|
Remove-Item $InstancePath -Recurse -Force
|
|
914
1184
|
}
|
|
@@ -962,6 +1232,65 @@ function Invoke-AuthCommands {
|
|
|
962
1232
|
}
|
|
963
1233
|
}
|
|
964
1234
|
|
|
1235
|
+
function Install-ShellCompletion {
|
|
1236
|
+
param([string[]]$Args)
|
|
1237
|
+
|
|
1238
|
+
Write-Host ""
|
|
1239
|
+
Write-Host "Shell Completion Installer" -ForegroundColor Yellow
|
|
1240
|
+
Write-Host ""
|
|
1241
|
+
|
|
1242
|
+
# Ensure completion directory exists
|
|
1243
|
+
$CompletionsDir = Join-Path $env:USERPROFILE ".ccs\completions"
|
|
1244
|
+
if (-not (Test-Path $CompletionsDir)) {
|
|
1245
|
+
New-Item -ItemType Directory -Path $CompletionsDir -Force | Out-Null
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
# Ensure completion file exists
|
|
1249
|
+
$CompletionFile = Join-Path $CompletionsDir "ccs.ps1"
|
|
1250
|
+
if (-not (Test-Path $CompletionFile)) {
|
|
1251
|
+
Write-Host "[X] Completion file not found. Please reinstall CCS." -ForegroundColor Red
|
|
1252
|
+
Write-Host ""
|
|
1253
|
+
exit 1
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
# Get PowerShell profile path
|
|
1257
|
+
$ProfilePath = $PROFILE
|
|
1258
|
+
$ProfileDir = Split-Path $ProfilePath -Parent
|
|
1259
|
+
|
|
1260
|
+
# Create profile directory if it doesn't exist
|
|
1261
|
+
if (-not (Test-Path $ProfileDir)) {
|
|
1262
|
+
New-Item -ItemType Directory -Path $ProfileDir -Force | Out-Null
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
# Comment marker for easy identification
|
|
1266
|
+
$Marker = "# CCS shell completion"
|
|
1267
|
+
$SourceCmd = ". `"$CompletionFile`""
|
|
1268
|
+
|
|
1269
|
+
# Check if already installed
|
|
1270
|
+
if (Test-Path $ProfilePath) {
|
|
1271
|
+
$Content = Get-Content $ProfilePath -Raw -ErrorAction SilentlyContinue
|
|
1272
|
+
if ($Content -and $Content.Contains($Marker)) {
|
|
1273
|
+
Write-Host "[OK] Shell completion already installed" -ForegroundColor Green
|
|
1274
|
+
Write-Host ""
|
|
1275
|
+
return 0
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
# Append to PowerShell profile
|
|
1280
|
+
$Block = "`n$Marker`n$SourceCmd`n"
|
|
1281
|
+
Add-Content -Path $ProfilePath -Value $Block -NoNewline
|
|
1282
|
+
|
|
1283
|
+
Write-Host "[OK] Shell completion installed successfully!" -ForegroundColor Green
|
|
1284
|
+
Write-Host ""
|
|
1285
|
+
Write-Host "Added to $ProfilePath"
|
|
1286
|
+
Write-Host ""
|
|
1287
|
+
Write-Host "To activate:" -ForegroundColor Cyan
|
|
1288
|
+
Write-Host " . `$PROFILE"
|
|
1289
|
+
Write-Host ""
|
|
1290
|
+
|
|
1291
|
+
return 0
|
|
1292
|
+
}
|
|
1293
|
+
|
|
965
1294
|
# --- Main Execution Logic ---
|
|
966
1295
|
|
|
967
1296
|
# Special case: version command (check BEFORE profile detection)
|
|
@@ -988,6 +1317,13 @@ if ($Help) {
|
|
|
988
1317
|
}
|
|
989
1318
|
}
|
|
990
1319
|
|
|
1320
|
+
# Special case: shell completion installer
|
|
1321
|
+
if ($RemainingArgs.Count -gt 0 -and $RemainingArgs[0] -eq "--shell-completion") {
|
|
1322
|
+
$CompletionArgs = if ($RemainingArgs.Count -gt 1) { $RemainingArgs[1..($RemainingArgs.Count-1)] } else { @() }
|
|
1323
|
+
$Result = Install-ShellCompletion $CompletionArgs
|
|
1324
|
+
exit $Result
|
|
1325
|
+
}
|
|
1326
|
+
|
|
991
1327
|
# Special case: auth commands
|
|
992
1328
|
if ($RemainingArgs.Count -gt 0 -and $RemainingArgs[0] -eq "auth") {
|
|
993
1329
|
$AuthArgs = if ($RemainingArgs.Count -gt 1) { $RemainingArgs[1..($RemainingArgs.Count-1)] } else { @() }
|
|
@@ -1025,11 +1361,36 @@ if ($Profile -notmatch '^[a-zA-Z0-9_-]+$') {
|
|
|
1025
1361
|
$ProfileInfo = Get-ProfileType $Profile
|
|
1026
1362
|
|
|
1027
1363
|
if ($ProfileInfo.Type -eq "error") {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1364
|
+
# Get suggestions using fuzzy matching
|
|
1365
|
+
$AllProfiles = Get-AllProfileNames
|
|
1366
|
+
$Suggestions = Find-SimilarStrings -Target $Profile -Candidates $AllProfiles
|
|
1031
1367
|
|
|
1032
|
-
Write-
|
|
1368
|
+
Write-Host ""
|
|
1369
|
+
Write-Host "[X] Profile '$Profile' not found" -ForegroundColor Red
|
|
1370
|
+
Write-Host ""
|
|
1371
|
+
|
|
1372
|
+
# Show suggestions if any
|
|
1373
|
+
if ($Suggestions -and $Suggestions.Count -gt 0) {
|
|
1374
|
+
Write-Host "Did you mean:" -ForegroundColor Yellow
|
|
1375
|
+
foreach ($suggestion in $Suggestions) {
|
|
1376
|
+
Write-Host " $suggestion"
|
|
1377
|
+
}
|
|
1378
|
+
Write-Host ""
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
Write-Host "Available profiles:" -ForegroundColor Cyan
|
|
1382
|
+
Get-AvailableProfiles | ForEach-Object { Write-Host $_ }
|
|
1383
|
+
Write-Host ""
|
|
1384
|
+
Write-Host "Solutions:" -ForegroundColor Yellow
|
|
1385
|
+
Write-Host " # Use existing profile"
|
|
1386
|
+
Write-Host " ccs <profile> `"your prompt`""
|
|
1387
|
+
Write-Host ""
|
|
1388
|
+
Write-Host " # Create new account profile"
|
|
1389
|
+
Write-Host " ccs auth create <name>"
|
|
1390
|
+
Write-Host ""
|
|
1391
|
+
Write-Host "Error: $script:E_PROFILE_NOT_FOUND" -ForegroundColor Yellow
|
|
1392
|
+
Write-Host (Get-ErrorDocUrl $script:E_PROFILE_NOT_FOUND) -ForegroundColor Yellow
|
|
1393
|
+
Write-Host ""
|
|
1033
1394
|
exit 1
|
|
1034
1395
|
}
|
|
1035
1396
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# CCS Error Codes
|
|
2
|
+
# Documentation: ../docs/errors/README.md
|
|
3
|
+
|
|
4
|
+
# Configuration Errors (E100-E199)
|
|
5
|
+
$script:E_CONFIG_MISSING = "E101"
|
|
6
|
+
$script:E_CONFIG_INVALID_JSON = "E102"
|
|
7
|
+
$script:E_CONFIG_INVALID_PROFILE = "E103"
|
|
8
|
+
|
|
9
|
+
# Profile Management Errors (E200-E299)
|
|
10
|
+
$script:E_PROFILE_NOT_FOUND = "E104"
|
|
11
|
+
$script:E_PROFILE_ALREADY_EXISTS = "E105"
|
|
12
|
+
$script:E_PROFILE_CANNOT_DELETE_DEFAULT = "E106"
|
|
13
|
+
$script:E_PROFILE_INVALID_NAME = "E107"
|
|
14
|
+
|
|
15
|
+
# Claude CLI Detection Errors (E300-E399)
|
|
16
|
+
$script:E_CLAUDE_NOT_FOUND = "E301"
|
|
17
|
+
$script:E_CLAUDE_VERSION_INCOMPATIBLE = "E302"
|
|
18
|
+
$script:E_CLAUDE_EXECUTION_FAILED = "E303"
|
|
19
|
+
|
|
20
|
+
# Network/API Errors (E400-E499)
|
|
21
|
+
$script:E_GLMT_PROXY_TIMEOUT = "E401"
|
|
22
|
+
$script:E_API_KEY_MISSING = "E402"
|
|
23
|
+
$script:E_API_AUTH_FAILED = "E403"
|
|
24
|
+
$script:E_API_RATE_LIMIT = "E404"
|
|
25
|
+
|
|
26
|
+
# File System Errors (E500-E599)
|
|
27
|
+
$script:E_FS_CANNOT_CREATE_DIR = "E501"
|
|
28
|
+
$script:E_FS_CANNOT_WRITE_FILE = "E502"
|
|
29
|
+
$script:E_FS_CANNOT_READ_FILE = "E503"
|
|
30
|
+
$script:E_FS_INSTANCE_NOT_FOUND = "E504"
|
|
31
|
+
|
|
32
|
+
# Internal Errors (E900-E999)
|
|
33
|
+
$script:E_INTERNAL_ERROR = "E900"
|
|
34
|
+
$script:E_INVALID_STATE = "E901"
|
|
35
|
+
|
|
36
|
+
# Get error documentation URL
|
|
37
|
+
function Get-ErrorDocUrl {
|
|
38
|
+
param([string]$ErrorCode)
|
|
39
|
+
return "https://github.com/kaitranntt/ccs/blob/main/docs/errors/README.md#$($ErrorCode.ToLower())"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Get error category from code
|
|
43
|
+
function Get-ErrorCategory {
|
|
44
|
+
param([string]$ErrorCode)
|
|
45
|
+
|
|
46
|
+
$code = [int]$ErrorCode.Substring(1)
|
|
47
|
+
|
|
48
|
+
if ($code -ge 100 -and $code -lt 200) { return "Configuration" }
|
|
49
|
+
elseif ($code -ge 200 -and $code -lt 300) { return "Profile Management" }
|
|
50
|
+
elseif ($code -ge 300 -and $code -lt 400) { return "Claude CLI Detection" }
|
|
51
|
+
elseif ($code -ge 400 -and $code -lt 500) { return "Network/API" }
|
|
52
|
+
elseif ($code -ge 500 -and $code -lt 600) { return "File System" }
|
|
53
|
+
elseif ($code -ge 900 -and $code -lt 1000) { return "Internal" }
|
|
54
|
+
else { return "Unknown" }
|
|
55
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CCS Error Codes
|
|
3
|
+
# Documentation: ../docs/errors/README.md
|
|
4
|
+
|
|
5
|
+
# Configuration Errors (E100-E199)
|
|
6
|
+
readonly E_CONFIG_MISSING="E101"
|
|
7
|
+
readonly E_CONFIG_INVALID_JSON="E102"
|
|
8
|
+
readonly E_CONFIG_INVALID_PROFILE="E103"
|
|
9
|
+
|
|
10
|
+
# Profile Management Errors (E200-E299)
|
|
11
|
+
readonly E_PROFILE_NOT_FOUND="E104"
|
|
12
|
+
readonly E_PROFILE_ALREADY_EXISTS="E105"
|
|
13
|
+
readonly E_PROFILE_CANNOT_DELETE_DEFAULT="E106"
|
|
14
|
+
readonly E_PROFILE_INVALID_NAME="E107"
|
|
15
|
+
|
|
16
|
+
# Claude CLI Detection Errors (E300-E399)
|
|
17
|
+
readonly E_CLAUDE_NOT_FOUND="E301"
|
|
18
|
+
readonly E_CLAUDE_VERSION_INCOMPATIBLE="E302"
|
|
19
|
+
readonly E_CLAUDE_EXECUTION_FAILED="E303"
|
|
20
|
+
|
|
21
|
+
# Network/API Errors (E400-E499)
|
|
22
|
+
readonly E_GLMT_PROXY_TIMEOUT="E401"
|
|
23
|
+
readonly E_API_KEY_MISSING="E402"
|
|
24
|
+
readonly E_API_AUTH_FAILED="E403"
|
|
25
|
+
readonly E_API_RATE_LIMIT="E404"
|
|
26
|
+
|
|
27
|
+
# File System Errors (E500-E599)
|
|
28
|
+
readonly E_FS_CANNOT_CREATE_DIR="E501"
|
|
29
|
+
readonly E_FS_CANNOT_WRITE_FILE="E502"
|
|
30
|
+
readonly E_FS_CANNOT_READ_FILE="E503"
|
|
31
|
+
readonly E_FS_INSTANCE_NOT_FOUND="E504"
|
|
32
|
+
|
|
33
|
+
# Internal Errors (E900-E999)
|
|
34
|
+
readonly E_INTERNAL_ERROR="E900"
|
|
35
|
+
readonly E_INVALID_STATE="E901"
|
|
36
|
+
|
|
37
|
+
# Get error documentation URL
|
|
38
|
+
get_error_doc_url() {
|
|
39
|
+
local error_code="$1"
|
|
40
|
+
echo "https://github.com/kaitranntt/ccs/blob/main/docs/errors/README.md#${error_code,,}"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Get error category from code
|
|
44
|
+
get_error_category() {
|
|
45
|
+
local error_code="$1"
|
|
46
|
+
local code="${error_code#E}"
|
|
47
|
+
|
|
48
|
+
if [[ $code -ge 100 && $code -lt 200 ]]; then
|
|
49
|
+
echo "Configuration"
|
|
50
|
+
elif [[ $code -ge 200 && $code -lt 300 ]]; then
|
|
51
|
+
echo "Profile Management"
|
|
52
|
+
elif [[ $code -ge 300 && $code -lt 400 ]]; then
|
|
53
|
+
echo "Claude CLI Detection"
|
|
54
|
+
elif [[ $code -ge 400 && $code -lt 500 ]]; then
|
|
55
|
+
echo "Network/API"
|
|
56
|
+
elif [[ $code -ge 500 && $code -lt 600 ]]; then
|
|
57
|
+
echo "File System"
|
|
58
|
+
elif [[ $code -ge 900 && $code -lt 1000 ]]; then
|
|
59
|
+
echo "Internal"
|
|
60
|
+
else
|
|
61
|
+
echo "Unknown"
|
|
62
|
+
fi
|
|
63
|
+
}
|