@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.
Files changed (47) hide show
  1. package/.claude/agents/ccs-delegator.md +117 -0
  2. package/.claude/commands/ccs/glm/continue.md +22 -0
  3. package/.claude/commands/ccs/glm.md +22 -0
  4. package/.claude/commands/ccs/kimi/continue.md +22 -0
  5. package/.claude/commands/ccs/kimi.md +22 -0
  6. package/.claude/skills/ccs-delegation/SKILL.md +54 -0
  7. package/.claude/skills/ccs-delegation/references/README.md +24 -0
  8. package/.claude/skills/ccs-delegation/references/delegation-guidelines.md +99 -0
  9. package/.claude/skills/ccs-delegation/references/headless-workflow.md +174 -0
  10. package/.claude/skills/ccs-delegation/references/troubleshooting.md +268 -0
  11. package/README.ja.md +470 -146
  12. package/README.md +532 -145
  13. package/README.vi.md +484 -157
  14. package/VERSION +1 -1
  15. package/bin/auth/auth-commands.js +98 -13
  16. package/bin/auth/profile-detector.js +11 -6
  17. package/bin/ccs.js +148 -2
  18. package/bin/delegation/README.md +189 -0
  19. package/bin/delegation/delegation-handler.js +212 -0
  20. package/bin/delegation/headless-executor.js +617 -0
  21. package/bin/delegation/result-formatter.js +483 -0
  22. package/bin/delegation/session-manager.js +156 -0
  23. package/bin/delegation/settings-parser.js +109 -0
  24. package/bin/management/doctor.js +94 -1
  25. package/bin/utils/claude-symlink-manager.js +238 -0
  26. package/bin/utils/delegation-validator.js +154 -0
  27. package/bin/utils/error-codes.js +59 -0
  28. package/bin/utils/error-manager.js +38 -32
  29. package/bin/utils/helpers.js +65 -1
  30. package/bin/utils/progress-indicator.js +111 -0
  31. package/bin/utils/prompt.js +134 -0
  32. package/bin/utils/shell-completion.js +234 -0
  33. package/lib/ccs +575 -25
  34. package/lib/ccs.ps1 +381 -20
  35. package/lib/error-codes.ps1 +55 -0
  36. package/lib/error-codes.sh +63 -0
  37. package/lib/progress-indicator.ps1 +120 -0
  38. package/lib/progress-indicator.sh +117 -0
  39. package/lib/prompt.ps1 +109 -0
  40. package/lib/prompt.sh +99 -0
  41. package/package.json +2 -1
  42. package/scripts/completion/README.md +308 -0
  43. package/scripts/completion/ccs.bash +81 -0
  44. package/scripts/completion/ccs.fish +92 -0
  45. package/scripts/completion/ccs.ps1 +157 -0
  46. package/scripts/completion/ccs.zsh +130 -0
  47. 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 = "3.4.6"
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 = $Args[0]
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`nUsage: ccs auth show <profile>"
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
- Write-Host " Last used: $LastUsed"
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 "--force") {
889
- $Force = $true
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`nUsage: ccs auth remove <profile> --force"
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
- if (-not $Force) {
906
- Write-ErrorMsg "Removal requires --force flag for safety`nRun: ccs auth remove $ProfileName --force"
907
- return 1
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
- $ErrorMessage = "Profile '$Profile' not found" + "`n`n" +
1029
- "Available profiles:" + "`n" +
1030
- (Get-AvailableProfiles)
1364
+ # Get suggestions using fuzzy matching
1365
+ $AllProfiles = Get-AllProfileNames
1366
+ $Suggestions = Find-SimilarStrings -Target $Profile -Candidates $AllProfiles
1031
1367
 
1032
- Write-ErrorMsg $ErrorMessage
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
+ }