@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
CHANGED
|
@@ -2,12 +2,32 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
# Version (updated by scripts/bump-version.sh)
|
|
5
|
-
CCS_VERSION="
|
|
5
|
+
CCS_VERSION="4.1.0"
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
7
|
readonly CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
8
8
|
readonly PROFILES_JSON="$HOME/.ccs/profiles.json"
|
|
9
9
|
readonly INSTANCES_DIR="$HOME/.ccs/instances"
|
|
10
10
|
|
|
11
|
+
# Determine dependency location (git vs installed)
|
|
12
|
+
# Git: lib/ccs and dependencies are in same dir (lib/)
|
|
13
|
+
# Installed: lib/ccs is symlinked from ~/.local/bin/ccs, dependencies in ~/.ccs/lib/
|
|
14
|
+
if [[ -f "$SCRIPT_DIR/error-codes.sh" ]]; then
|
|
15
|
+
# Git install - files in same directory
|
|
16
|
+
DEP_DIR="$SCRIPT_DIR"
|
|
17
|
+
else
|
|
18
|
+
# Standalone install - files in ~/.ccs/lib/
|
|
19
|
+
DEP_DIR="$HOME/.ccs/lib"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Source error codes
|
|
23
|
+
source "$DEP_DIR/error-codes.sh"
|
|
24
|
+
|
|
25
|
+
# Source progress indicators
|
|
26
|
+
source "$DEP_DIR/progress-indicator.sh"
|
|
27
|
+
|
|
28
|
+
# Source interactive prompts
|
|
29
|
+
source "$DEP_DIR/prompt.sh"
|
|
30
|
+
|
|
11
31
|
# --- Color/Format Functions ---
|
|
12
32
|
setup_colors() {
|
|
13
33
|
# Enable colors if: FORCE_COLOR set OR (TTY detected AND NO_COLOR not set) OR (TERM supports colors AND NO_COLOR not set)
|
|
@@ -27,14 +47,121 @@ setup_colors() {
|
|
|
27
47
|
|
|
28
48
|
msg_error() {
|
|
29
49
|
echo "" >&2
|
|
30
|
-
echo -e "${RED}${BOLD}
|
|
31
|
-
echo -e "${RED}${BOLD}
|
|
32
|
-
echo -e "${RED}${BOLD}
|
|
50
|
+
echo -e "${RED}${BOLD}=============================================${RESET}" >&2
|
|
51
|
+
echo -e "${RED}${BOLD} ERROR${RESET}" >&2
|
|
52
|
+
echo -e "${RED}${BOLD}=============================================${RESET}" >&2
|
|
33
53
|
echo "" >&2
|
|
34
54
|
echo -e "${RED}$1${RESET}" >&2
|
|
35
55
|
echo "" >&2
|
|
36
56
|
}
|
|
37
57
|
|
|
58
|
+
# Enhanced error message with error codes
|
|
59
|
+
show_enhanced_error() {
|
|
60
|
+
local error_code="$1"
|
|
61
|
+
local short_msg="$2"
|
|
62
|
+
local context="${3:-}"
|
|
63
|
+
local suggestions="${4:-}"
|
|
64
|
+
|
|
65
|
+
echo "" >&2
|
|
66
|
+
echo -e "${RED}[X] $short_msg${RESET}" >&2
|
|
67
|
+
echo "" >&2
|
|
68
|
+
|
|
69
|
+
[[ -n "$context" ]] && {
|
|
70
|
+
echo -e "$context" >&2
|
|
71
|
+
echo "" >&2
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[[ -n "$suggestions" ]] && {
|
|
75
|
+
echo -e "${YELLOW}Solutions:${RESET}" >&2
|
|
76
|
+
echo -e "$suggestions" >&2
|
|
77
|
+
echo "" >&2
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
echo -e "${YELLOW}Error: $error_code${RESET}" >&2
|
|
81
|
+
echo -e "${YELLOW}$(get_error_doc_url "$error_code")${RESET}" >&2
|
|
82
|
+
echo "" >&2
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Calculate Levenshtein distance between two strings
|
|
86
|
+
levenshtein_distance() {
|
|
87
|
+
local a="$1"
|
|
88
|
+
local b="$2"
|
|
89
|
+
local len_a=${#a}
|
|
90
|
+
local len_b=${#b}
|
|
91
|
+
|
|
92
|
+
# Return early for empty strings
|
|
93
|
+
[[ $len_a -eq 0 ]] && { echo "$len_b"; return; }
|
|
94
|
+
[[ $len_b -eq 0 ]] && { echo "$len_a"; return; }
|
|
95
|
+
|
|
96
|
+
# Initialize matrix using associative array
|
|
97
|
+
declare -A matrix
|
|
98
|
+
|
|
99
|
+
# Initialize first row and column
|
|
100
|
+
for ((j=0; j<=len_a; j++)); do
|
|
101
|
+
matrix[0,$j]=$j
|
|
102
|
+
done
|
|
103
|
+
for ((i=0; i<=len_b; i++)); do
|
|
104
|
+
matrix[$i,0]=$i
|
|
105
|
+
done
|
|
106
|
+
|
|
107
|
+
# Fill matrix
|
|
108
|
+
for ((i=1; i<=len_b; i++)); do
|
|
109
|
+
for ((j=1; j<=len_a; j++)); do
|
|
110
|
+
if [[ "${a:j-1:1}" == "${b:i-1:1}" ]]; then
|
|
111
|
+
matrix[$i,$j]=${matrix[$((i-1)),$((j-1))]}
|
|
112
|
+
else
|
|
113
|
+
local sub=${matrix[$((i-1)),$((j-1))]}
|
|
114
|
+
local ins=${matrix[$i,$((j-1))]}
|
|
115
|
+
local del=${matrix[$((i-1)),$j]}
|
|
116
|
+
local min=$sub
|
|
117
|
+
[[ $ins -lt $min ]] && min=$ins
|
|
118
|
+
[[ $del -lt $min ]] && min=$del
|
|
119
|
+
matrix[$i,$j]=$((min + 1))
|
|
120
|
+
fi
|
|
121
|
+
done
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
echo "${matrix[$len_b,$len_a]}"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Find similar strings using fuzzy matching
|
|
128
|
+
find_similar_strings() {
|
|
129
|
+
local target="$1"
|
|
130
|
+
shift
|
|
131
|
+
local candidates=("$@")
|
|
132
|
+
local max_distance=2
|
|
133
|
+
local target_lower="${target,,}"
|
|
134
|
+
|
|
135
|
+
declare -A distances
|
|
136
|
+
local matches=()
|
|
137
|
+
|
|
138
|
+
# Calculate distances
|
|
139
|
+
for candidate in "${candidates[@]}"; do
|
|
140
|
+
local candidate_lower="${candidate,,}"
|
|
141
|
+
local dist=$(levenshtein_distance "$target_lower" "$candidate_lower")
|
|
142
|
+
if [[ $dist -le $max_distance && $dist -gt 0 ]]; then
|
|
143
|
+
distances["$candidate"]=$dist
|
|
144
|
+
matches+=("$candidate")
|
|
145
|
+
fi
|
|
146
|
+
done
|
|
147
|
+
|
|
148
|
+
# Sort by distance (simple bubble sort for small arrays)
|
|
149
|
+
for ((i=0; i<${#matches[@]}; i++)); do
|
|
150
|
+
for ((j=i+1; j<${#matches[@]}; j++)); do
|
|
151
|
+
if [[ ${distances[${matches[i]}]} -gt ${distances[${matches[j]}]} ]]; then
|
|
152
|
+
local temp="${matches[i]}"
|
|
153
|
+
matches[i]="${matches[j]}"
|
|
154
|
+
matches[j]="$temp"
|
|
155
|
+
fi
|
|
156
|
+
done
|
|
157
|
+
done
|
|
158
|
+
|
|
159
|
+
# Return first 3 matches
|
|
160
|
+
for ((i=0; i<${#matches[@]} && i<3; i++)); do
|
|
161
|
+
echo "${matches[i]}"
|
|
162
|
+
done
|
|
163
|
+
}
|
|
164
|
+
|
|
38
165
|
show_help() {
|
|
39
166
|
echo -e "${BOLD}CCS (Claude Code Switch) - Instant profile switching for Claude CLI${RESET}"
|
|
40
167
|
echo ""
|
|
@@ -60,12 +187,20 @@ show_help() {
|
|
|
60
187
|
echo -e " ${YELLOW}ccs work${RESET} Switch to work account"
|
|
61
188
|
echo -e " ${YELLOW}ccs personal${RESET} Switch to personal account"
|
|
62
189
|
echo ""
|
|
190
|
+
echo -e "${CYAN}Delegation (Token Optimization):${RESET}"
|
|
191
|
+
echo -e " ${YELLOW}/ccs:glm \"task\"${RESET} Delegate to GLM-4.6 within Claude session"
|
|
192
|
+
echo -e " ${YELLOW}/ccs:kimi \"task\"${RESET} Delegate to Kimi for long context"
|
|
193
|
+
echo -e " ${YELLOW}/ccs:create m2${RESET} Create custom delegation command"
|
|
194
|
+
echo -e " Use delegation to save tokens on simple tasks"
|
|
195
|
+
echo -e " Commands work inside Claude Code sessions only"
|
|
196
|
+
echo ""
|
|
63
197
|
echo -e "${CYAN}Diagnostics:${RESET}"
|
|
64
198
|
echo -e " ${YELLOW}ccs doctor${RESET} Run health check and diagnostics"
|
|
65
199
|
echo ""
|
|
66
200
|
echo -e "${CYAN}Flags:${RESET}"
|
|
67
201
|
echo -e " ${YELLOW}-h, --help${RESET} Show this help message"
|
|
68
202
|
echo -e " ${YELLOW}-v, --version${RESET} Show version and installation info"
|
|
203
|
+
echo -e " ${YELLOW}--shell-completion${RESET} Install shell auto-completion"
|
|
69
204
|
echo ""
|
|
70
205
|
echo -e "${CYAN}Configuration:${RESET}"
|
|
71
206
|
echo -e " Config: ~/.ccs/config.json"
|
|
@@ -78,6 +213,22 @@ show_help() {
|
|
|
78
213
|
echo -e " Skills: ~/.ccs/shared/skills/"
|
|
79
214
|
echo -e " Note: Commands, skills, and agents are symlinked across all profiles"
|
|
80
215
|
echo ""
|
|
216
|
+
echo -e "${CYAN}Examples:${RESET}"
|
|
217
|
+
echo -e " Quick start:"
|
|
218
|
+
echo -e " ${YELLOW}\$ ccs${RESET} # Use default account"
|
|
219
|
+
echo -e " ${YELLOW}\$ ccs glm \"implement API\"${RESET} # Cost-optimized model"
|
|
220
|
+
echo ""
|
|
221
|
+
echo -e " Multi-account workflow:"
|
|
222
|
+
echo -e " ${YELLOW}\$ ccs auth create work${RESET} # Create work profile"
|
|
223
|
+
echo -e " ${YELLOW}\$ ccs work \"review PR\"${RESET} # Use work account"
|
|
224
|
+
echo ""
|
|
225
|
+
echo -e " For more: ${CYAN}https://github.com/kaitranntt/ccs#usage${RESET}"
|
|
226
|
+
echo ""
|
|
227
|
+
echo -e "${YELLOW}Uninstall:${RESET}"
|
|
228
|
+
echo " npm: npm uninstall -g @kaitranntt/ccs"
|
|
229
|
+
echo " macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash"
|
|
230
|
+
echo " Windows: irm ccs.kaitran.ca/uninstall | iex"
|
|
231
|
+
echo ""
|
|
81
232
|
echo -e "${CYAN}Documentation:${RESET}"
|
|
82
233
|
echo -e " GitHub: ${CYAN}https://github.com/kaitranntt/ccs${RESET}"
|
|
83
234
|
echo -e " Docs: https://github.com/kaitranntt/ccs/blob/main/README.md"
|
|
@@ -189,96 +340,137 @@ doctor_run() {
|
|
|
189
340
|
echo ""
|
|
190
341
|
|
|
191
342
|
local has_errors=false
|
|
343
|
+
local total_checks=9
|
|
344
|
+
local current_check=0
|
|
192
345
|
|
|
193
346
|
# Check Claude CLI
|
|
347
|
+
current_check=$((current_check + 1))
|
|
348
|
+
show_progress_step $current_check $total_checks "Checking Claude CLI"
|
|
194
349
|
if command -v "$(detect_claude_cli)" &>/dev/null; then
|
|
350
|
+
clear_progress
|
|
195
351
|
doctor_check "Claude CLI" "success"
|
|
196
352
|
else
|
|
353
|
+
clear_progress
|
|
197
354
|
doctor_check "Claude CLI" "error" "Not found in PATH"
|
|
198
355
|
has_errors=true
|
|
199
356
|
fi
|
|
200
357
|
|
|
201
358
|
# Check ~/.ccs/
|
|
359
|
+
current_check=$((current_check + 1))
|
|
360
|
+
show_progress_step $current_check $total_checks "Checking CCS directory"
|
|
202
361
|
if [[ -d "$HOME/.ccs" ]]; then
|
|
362
|
+
clear_progress
|
|
203
363
|
doctor_check "CCS Directory" "success"
|
|
204
364
|
else
|
|
365
|
+
clear_progress
|
|
205
366
|
doctor_check "CCS Directory" "error" "~/.ccs/ not found"
|
|
206
367
|
has_errors=true
|
|
207
368
|
fi
|
|
208
369
|
|
|
209
370
|
# Check config.json
|
|
371
|
+
current_check=$((current_check + 1))
|
|
372
|
+
show_progress_step $current_check $total_checks "Checking config.json"
|
|
210
373
|
if [[ -f "$CONFIG_FILE" ]]; then
|
|
211
374
|
if jq empty "$CONFIG_FILE" 2>/dev/null; then
|
|
375
|
+
clear_progress
|
|
212
376
|
doctor_check "config.json" "success"
|
|
213
377
|
else
|
|
378
|
+
clear_progress
|
|
214
379
|
doctor_check "config.json" "error" "Invalid JSON"
|
|
215
380
|
has_errors=true
|
|
216
381
|
fi
|
|
217
382
|
else
|
|
383
|
+
clear_progress
|
|
218
384
|
doctor_check "config.json" "error" "Not found"
|
|
219
385
|
has_errors=true
|
|
220
386
|
fi
|
|
221
387
|
|
|
222
388
|
# Check glm.settings.json
|
|
389
|
+
current_check=$((current_check + 1))
|
|
390
|
+
show_progress_step $current_check $total_checks "Checking glm.settings.json"
|
|
223
391
|
local glm_file="$HOME/.ccs/glm.settings.json"
|
|
224
392
|
if [[ -f "$glm_file" ]]; then
|
|
225
393
|
if jq empty "$glm_file" 2>/dev/null; then
|
|
394
|
+
clear_progress
|
|
226
395
|
doctor_check "glm.settings.json" "success"
|
|
227
396
|
else
|
|
397
|
+
clear_progress
|
|
228
398
|
doctor_check "glm.settings.json" "error" "Invalid JSON"
|
|
229
399
|
has_errors=true
|
|
230
400
|
fi
|
|
231
401
|
else
|
|
402
|
+
clear_progress
|
|
232
403
|
doctor_check "glm.settings.json" "error" "Not found"
|
|
233
404
|
has_errors=true
|
|
234
405
|
fi
|
|
235
406
|
|
|
236
407
|
# Check kimi.settings.json
|
|
408
|
+
current_check=$((current_check + 1))
|
|
409
|
+
show_progress_step $current_check $total_checks "Checking kimi.settings.json"
|
|
237
410
|
local kimi_file="$HOME/.ccs/kimi.settings.json"
|
|
238
411
|
if [[ -f "$kimi_file" ]]; then
|
|
239
412
|
if jq empty "$kimi_file" 2>/dev/null; then
|
|
413
|
+
clear_progress
|
|
240
414
|
doctor_check "kimi.settings.json" "success"
|
|
241
415
|
else
|
|
416
|
+
clear_progress
|
|
242
417
|
doctor_check "kimi.settings.json" "error" "Invalid JSON"
|
|
243
418
|
has_errors=true
|
|
244
419
|
fi
|
|
245
420
|
else
|
|
421
|
+
clear_progress
|
|
246
422
|
doctor_check "kimi.settings.json" "error" "Not found"
|
|
247
423
|
has_errors=true
|
|
248
424
|
fi
|
|
249
425
|
|
|
250
426
|
# Check ~/.claude/settings.json
|
|
427
|
+
current_check=$((current_check + 1))
|
|
428
|
+
show_progress_step $current_check $total_checks "Checking Claude settings"
|
|
251
429
|
if [[ -f "$HOME/.claude/settings.json" ]]; then
|
|
252
430
|
if jq empty "$HOME/.claude/settings.json" 2>/dev/null; then
|
|
431
|
+
clear_progress
|
|
253
432
|
doctor_check "Claude Settings" "success"
|
|
254
433
|
else
|
|
434
|
+
clear_progress
|
|
255
435
|
doctor_check "Claude Settings" "warning" "Invalid JSON"
|
|
256
436
|
fi
|
|
257
437
|
else
|
|
438
|
+
clear_progress
|
|
258
439
|
doctor_check "Claude Settings" "warning" "Not found - run 'claude /login'"
|
|
259
440
|
fi
|
|
260
441
|
|
|
261
442
|
# Check profiles
|
|
443
|
+
current_check=$((current_check + 1))
|
|
444
|
+
show_progress_step $current_check $total_checks "Checking profiles"
|
|
262
445
|
if [[ -f "$CONFIG_FILE" ]]; then
|
|
263
446
|
local profile_count=$(jq -r '.profiles | length' "$CONFIG_FILE" 2>/dev/null || echo "0")
|
|
447
|
+
clear_progress
|
|
264
448
|
doctor_check "Profiles" "success" "($profile_count configured)"
|
|
265
449
|
fi
|
|
266
450
|
|
|
267
451
|
# Check instances
|
|
452
|
+
current_check=$((current_check + 1))
|
|
453
|
+
show_progress_step $current_check $total_checks "Checking instances"
|
|
268
454
|
if [[ -d "$INSTANCES_DIR" ]]; then
|
|
269
455
|
local instance_count=$(find "$INSTANCES_DIR" -maxdepth 1 -type d 2>/dev/null | wc -l)
|
|
270
456
|
instance_count=$((instance_count - 1)) # Exclude parent dir
|
|
457
|
+
clear_progress
|
|
271
458
|
doctor_check "Instances" "success" "($instance_count account profiles)"
|
|
272
459
|
else
|
|
460
|
+
clear_progress
|
|
273
461
|
doctor_check "Instances" "success" "(no account profiles)"
|
|
274
462
|
fi
|
|
275
463
|
|
|
276
464
|
# Check permissions
|
|
465
|
+
current_check=$((current_check + 1))
|
|
466
|
+
show_progress_step $current_check $total_checks "Checking permissions"
|
|
277
467
|
local test_file="$HOME/.ccs/.permission-test"
|
|
278
468
|
if echo "test" > "$test_file" 2>/dev/null; then
|
|
279
469
|
rm -f "$test_file" 2>/dev/null
|
|
470
|
+
clear_progress
|
|
280
471
|
doctor_check "Permissions" "success"
|
|
281
472
|
else
|
|
473
|
+
clear_progress
|
|
282
474
|
doctor_check "Permissions" "error" "Cannot write to ~/.ccs/"
|
|
283
475
|
has_errors=true
|
|
284
476
|
fi
|
|
@@ -337,6 +529,33 @@ show_version() {
|
|
|
337
529
|
# Simple config display
|
|
338
530
|
local config="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
339
531
|
echo -e " ${CYAN}Config:${RESET} ${config}"
|
|
532
|
+
|
|
533
|
+
# Delegation status
|
|
534
|
+
local delegation_rules="$HOME/.ccs/delegation-rules.json"
|
|
535
|
+
if [[ -f "$delegation_rules" ]]; then
|
|
536
|
+
echo -e " ${CYAN}Delegation:${RESET} Enabled"
|
|
537
|
+
|
|
538
|
+
# Check which profiles are delegation-ready
|
|
539
|
+
local ready_profiles=()
|
|
540
|
+
for profile in glm kimi; do
|
|
541
|
+
local settings_file="$HOME/.ccs/profiles/$profile/settings.json"
|
|
542
|
+
if [[ -f "$settings_file" ]]; then
|
|
543
|
+
# Check if API key is configured (not a placeholder)
|
|
544
|
+
local api_key=$(jq -r '.env.ANTHROPIC_AUTH_TOKEN // empty' "$settings_file" 2>/dev/null)
|
|
545
|
+
if [[ -n "$api_key" ]] && [[ ! "$api_key" =~ YOUR_.*_API_KEY_HERE ]]; then
|
|
546
|
+
ready_profiles+=("$profile")
|
|
547
|
+
fi
|
|
548
|
+
fi
|
|
549
|
+
done
|
|
550
|
+
|
|
551
|
+
if [[ ${#ready_profiles[@]} -gt 0 ]]; then
|
|
552
|
+
echo -e " ${CYAN}Ready:${RESET} ${ready_profiles[*]}"
|
|
553
|
+
else
|
|
554
|
+
echo -e " ${CYAN}Ready:${RESET} None (configure profiles first)"
|
|
555
|
+
fi
|
|
556
|
+
else
|
|
557
|
+
echo -e " ${CYAN}Delegation:${RESET} Not configured"
|
|
558
|
+
fi
|
|
340
559
|
echo ""
|
|
341
560
|
|
|
342
561
|
echo -e "${CYAN}Documentation:${RESET} https://github.com/kaitranntt/ccs"
|
|
@@ -629,6 +848,27 @@ ensure_instance() {
|
|
|
629
848
|
# --- Profile Detection Logic (Phase 1) ---
|
|
630
849
|
|
|
631
850
|
# List available profiles for error messages
|
|
851
|
+
# Get all profile names (for fuzzy matching)
|
|
852
|
+
get_all_profile_names() {
|
|
853
|
+
local names=()
|
|
854
|
+
|
|
855
|
+
# Settings-based profiles
|
|
856
|
+
if [[ -f "$CONFIG_FILE" ]]; then
|
|
857
|
+
while IFS= read -r name; do
|
|
858
|
+
names+=("$name")
|
|
859
|
+
done < <(jq -r '.profiles | keys[]' "$CONFIG_FILE" 2>/dev/null || true)
|
|
860
|
+
fi
|
|
861
|
+
|
|
862
|
+
# Account-based profiles
|
|
863
|
+
if [[ -f "$PROFILES_JSON" ]]; then
|
|
864
|
+
while IFS= read -r name; do
|
|
865
|
+
names+=("$name")
|
|
866
|
+
done < <(jq -r '.profiles | keys[]' "$PROFILES_JSON" 2>/dev/null || true)
|
|
867
|
+
fi
|
|
868
|
+
|
|
869
|
+
printf '%s\n' "${names[@]}"
|
|
870
|
+
}
|
|
871
|
+
|
|
632
872
|
list_available_profiles() {
|
|
633
873
|
local lines=()
|
|
634
874
|
|
|
@@ -745,6 +985,12 @@ auth_help() {
|
|
|
745
985
|
echo -e " ${YELLOW}ccs work \"review code\"${RESET} # Use work profile"
|
|
746
986
|
echo -e " ${YELLOW}ccs \"review code\"${RESET} # Use default profile"
|
|
747
987
|
echo ""
|
|
988
|
+
echo -e "${CYAN}Options:${RESET}"
|
|
989
|
+
echo -e " ${YELLOW}--force${RESET} Allow overwriting existing profile (create)"
|
|
990
|
+
echo -e " ${YELLOW}--yes, -y${RESET} Skip confirmation prompts (remove)"
|
|
991
|
+
echo -e " ${YELLOW}--json${RESET} Output in JSON format (list, show)"
|
|
992
|
+
echo -e " ${YELLOW}--verbose${RESET} Show additional details (list)"
|
|
993
|
+
echo ""
|
|
748
994
|
echo -e "${CYAN}Note:${RESET}"
|
|
749
995
|
echo -e " By default, ${YELLOW}ccs${RESET} uses Claude CLI defaults from ~/.claude/"
|
|
750
996
|
echo -e " Use ${YELLOW}ccs auth default <profile>${RESET} to change the default profile."
|
|
@@ -816,10 +1062,24 @@ auth_create() {
|
|
|
816
1062
|
|
|
817
1063
|
auth_list() {
|
|
818
1064
|
local verbose=false
|
|
819
|
-
|
|
1065
|
+
local json=false
|
|
1066
|
+
|
|
1067
|
+
# Parse arguments
|
|
1068
|
+
while [[ $# -gt 0 ]]; do
|
|
1069
|
+
case "$1" in
|
|
1070
|
+
--verbose) verbose=true ;;
|
|
1071
|
+
--json) json=true ;;
|
|
1072
|
+
*) ;;
|
|
1073
|
+
esac
|
|
1074
|
+
shift
|
|
1075
|
+
done
|
|
820
1076
|
|
|
821
1077
|
# Read profiles.json
|
|
822
1078
|
[[ ! -f "$PROFILES_JSON" ]] && {
|
|
1079
|
+
if $json; then
|
|
1080
|
+
echo "{\"version\":\"$CCS_VERSION\",\"profiles\":[]}"
|
|
1081
|
+
return 0
|
|
1082
|
+
fi
|
|
823
1083
|
echo -e "${YELLOW}No account profiles found${RESET}"
|
|
824
1084
|
echo ""
|
|
825
1085
|
echo "To create your first profile:"
|
|
@@ -827,9 +1087,33 @@ auth_list() {
|
|
|
827
1087
|
return 0
|
|
828
1088
|
}
|
|
829
1089
|
|
|
830
|
-
local profiles=$(jq -r '.profiles | keys[]' "$PROFILES_JSON" 2>/dev/null || true)
|
|
831
1090
|
local default_profile=$(jq -r '.default // empty' "$PROFILES_JSON" 2>/dev/null || true)
|
|
832
1091
|
|
|
1092
|
+
# JSON output mode
|
|
1093
|
+
if $json; then
|
|
1094
|
+
jq -n \
|
|
1095
|
+
--arg version "$CCS_VERSION" \
|
|
1096
|
+
--arg default "$default_profile" \
|
|
1097
|
+
--argjson data "$(cat "$PROFILES_JSON")" \
|
|
1098
|
+
'{
|
|
1099
|
+
version: $version,
|
|
1100
|
+
profiles: [
|
|
1101
|
+
$data.profiles | to_entries[] | {
|
|
1102
|
+
name: .key,
|
|
1103
|
+
type: (.value.type // "account"),
|
|
1104
|
+
is_default: (.key == $default),
|
|
1105
|
+
created: .value.created,
|
|
1106
|
+
last_used: (.value.last_used // null),
|
|
1107
|
+
instance_path: ($ENV.INSTANCES_DIR + "/" + .key)
|
|
1108
|
+
}
|
|
1109
|
+
]
|
|
1110
|
+
}'
|
|
1111
|
+
return 0
|
|
1112
|
+
fi
|
|
1113
|
+
|
|
1114
|
+
# Human-readable output
|
|
1115
|
+
local profiles=$(jq -r '.profiles | keys[]' "$PROFILES_JSON" 2>/dev/null || true)
|
|
1116
|
+
|
|
833
1117
|
[[ -z "$profiles" ]] && {
|
|
834
1118
|
echo -e "${YELLOW}No account profiles found${RESET}"
|
|
835
1119
|
return 0
|
|
@@ -864,11 +1148,22 @@ auth_list() {
|
|
|
864
1148
|
}
|
|
865
1149
|
|
|
866
1150
|
auth_show() {
|
|
867
|
-
local profile_name="
|
|
1151
|
+
local profile_name=""
|
|
1152
|
+
local json=false
|
|
1153
|
+
|
|
1154
|
+
# Parse arguments
|
|
1155
|
+
while [[ $# -gt 0 ]]; do
|
|
1156
|
+
case "$1" in
|
|
1157
|
+
--json) json=true ;;
|
|
1158
|
+
-*) msg_error "Unknown option: $1"; return 1 ;;
|
|
1159
|
+
*) profile_name="$1" ;;
|
|
1160
|
+
esac
|
|
1161
|
+
shift
|
|
1162
|
+
done
|
|
868
1163
|
|
|
869
1164
|
[[ -z "$profile_name" ]] && {
|
|
870
1165
|
msg_error "Profile name is required"
|
|
871
|
-
echo "Usage: ${YELLOW}ccs auth show <profile
|
|
1166
|
+
echo "Usage: ${YELLOW}ccs auth show <profile> [--json]${RESET}"
|
|
872
1167
|
return 1
|
|
873
1168
|
}
|
|
874
1169
|
|
|
@@ -882,29 +1177,59 @@ auth_show() {
|
|
|
882
1177
|
local is_default=false
|
|
883
1178
|
[[ "$profile_name" == "$default_profile" ]] && is_default=true
|
|
884
1179
|
|
|
885
|
-
echo -e "${BOLD}Profile: $profile_name${RESET}"
|
|
886
|
-
echo ""
|
|
887
|
-
|
|
888
1180
|
local type=$(jq -r ".profiles.\"$profile_name\".type // \"account\"" "$PROFILES_JSON" 2>/dev/null || true)
|
|
889
1181
|
local created=$(jq -r ".profiles.\"$profile_name\".created" "$PROFILES_JSON" 2>/dev/null || true)
|
|
890
|
-
local last_used=$(jq -r ".profiles.\"$profile_name\".last_used //
|
|
1182
|
+
local last_used=$(jq -r ".profiles.\"$profile_name\".last_used // null" "$PROFILES_JSON" 2>/dev/null || true)
|
|
891
1183
|
local instance_path="$INSTANCES_DIR/$(sanitize_profile_name "$profile_name")"
|
|
892
1184
|
|
|
1185
|
+
# Count sessions
|
|
1186
|
+
local session_count=0
|
|
1187
|
+
if [[ -d "$instance_path/session-env" ]]; then
|
|
1188
|
+
session_count=$(find "$instance_path/session-env" -name "*.json" 2>/dev/null | wc -l | tr -d ' ')
|
|
1189
|
+
fi
|
|
1190
|
+
|
|
1191
|
+
# JSON output mode
|
|
1192
|
+
if $json; then
|
|
1193
|
+
jq -n \
|
|
1194
|
+
--arg name "$profile_name" \
|
|
1195
|
+
--arg type "$type" \
|
|
1196
|
+
--argjson is_default "$is_default" \
|
|
1197
|
+
--arg created "$created" \
|
|
1198
|
+
--arg last_used "$last_used" \
|
|
1199
|
+
--arg instance_path "$instance_path" \
|
|
1200
|
+
--argjson session_count "$session_count" \
|
|
1201
|
+
'{
|
|
1202
|
+
name: $name,
|
|
1203
|
+
type: $type,
|
|
1204
|
+
is_default: $is_default,
|
|
1205
|
+
created: $created,
|
|
1206
|
+
last_used: $last_used,
|
|
1207
|
+
instance_path: $instance_path,
|
|
1208
|
+
session_count: $session_count
|
|
1209
|
+
}'
|
|
1210
|
+
return 0
|
|
1211
|
+
fi
|
|
1212
|
+
|
|
1213
|
+
# Human-readable output
|
|
1214
|
+
echo -e "${BOLD}Profile: $profile_name${RESET}"
|
|
1215
|
+
echo ""
|
|
1216
|
+
|
|
893
1217
|
echo " Type: $type"
|
|
894
1218
|
echo " Default: $($is_default && echo "Yes" || echo "No")"
|
|
895
1219
|
echo " Instance: $instance_path"
|
|
896
1220
|
echo " Created: $created"
|
|
897
|
-
echo " Last used: $last_used"
|
|
1221
|
+
[[ "$last_used" != "null" ]] && echo " Last used: $last_used" || echo " Last used: Never"
|
|
898
1222
|
echo ""
|
|
899
1223
|
}
|
|
900
1224
|
|
|
901
1225
|
auth_remove() {
|
|
902
1226
|
local profile_name=""
|
|
903
|
-
local force=false
|
|
904
1227
|
|
|
1228
|
+
# Parse arguments
|
|
905
1229
|
while [[ $# -gt 0 ]]; do
|
|
906
1230
|
case "$1" in
|
|
907
|
-
--
|
|
1231
|
+
--yes|-y) export CCS_YES=1 ;; # Auto-confirm (export for confirm_action)
|
|
1232
|
+
-*) msg_error "Unknown option: $1"; return 1 ;;
|
|
908
1233
|
*) profile_name="$1" ;;
|
|
909
1234
|
esac
|
|
910
1235
|
shift
|
|
@@ -912,7 +1237,8 @@ auth_remove() {
|
|
|
912
1237
|
|
|
913
1238
|
[[ -z "$profile_name" ]] && {
|
|
914
1239
|
msg_error "Profile name is required"
|
|
915
|
-
echo "
|
|
1240
|
+
echo ""
|
|
1241
|
+
echo "Usage: ${YELLOW}ccs auth remove <profile> [--yes]${RESET}"
|
|
916
1242
|
return 1
|
|
917
1243
|
}
|
|
918
1244
|
|
|
@@ -921,14 +1247,28 @@ auth_remove() {
|
|
|
921
1247
|
return 1
|
|
922
1248
|
}
|
|
923
1249
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1250
|
+
# Get instance path and session count for impact display
|
|
1251
|
+
local instance_path="$INSTANCES_DIR/$(sanitize_profile_name "$profile_name")"
|
|
1252
|
+
local session_count=0
|
|
1253
|
+
|
|
1254
|
+
if [[ -d "$instance_path/session-env" ]]; then
|
|
1255
|
+
session_count=$(find "$instance_path/session-env" -name "*.json" 2>/dev/null | wc -l | tr -d ' ')
|
|
1256
|
+
fi
|
|
1257
|
+
|
|
1258
|
+
# Display impact
|
|
1259
|
+
echo ""
|
|
1260
|
+
echo "Profile '${CYAN}$profile_name${RESET}' will be permanently deleted."
|
|
1261
|
+
echo " Instance path: $instance_path"
|
|
1262
|
+
echo " Sessions: $session_count conversation$([ "$session_count" -ne 1 ] && echo "s" || echo "")"
|
|
1263
|
+
echo ""
|
|
1264
|
+
|
|
1265
|
+
# Interactive confirmation (or --yes flag)
|
|
1266
|
+
if ! confirm_action "Delete this profile?" "no"; then
|
|
1267
|
+
echo "[i] Cancelled"
|
|
1268
|
+
return 0
|
|
1269
|
+
fi
|
|
929
1270
|
|
|
930
1271
|
# Delete instance directory
|
|
931
|
-
local instance_path="$INSTANCES_DIR/$(sanitize_profile_name "$profile_name")"
|
|
932
1272
|
rm -rf "$instance_path"
|
|
933
1273
|
|
|
934
1274
|
# Remove from registry
|
|
@@ -979,6 +1319,184 @@ handle_auth_commands() {
|
|
|
979
1319
|
esac
|
|
980
1320
|
}
|
|
981
1321
|
|
|
1322
|
+
# --- Shell Completion Installer ---
|
|
1323
|
+
|
|
1324
|
+
install_shell_completion() {
|
|
1325
|
+
shift # Remove --shell-completion
|
|
1326
|
+
|
|
1327
|
+
echo -e "${BOLD}Shell Completion Installer${RESET}"
|
|
1328
|
+
echo ""
|
|
1329
|
+
|
|
1330
|
+
# Parse flags for manual shell selection
|
|
1331
|
+
local target_shell=""
|
|
1332
|
+
for arg in "$@"; do
|
|
1333
|
+
case "$arg" in
|
|
1334
|
+
--bash) target_shell="bash" ;;
|
|
1335
|
+
--zsh) target_shell="zsh" ;;
|
|
1336
|
+
--fish) target_shell="fish" ;;
|
|
1337
|
+
*) ;;
|
|
1338
|
+
esac
|
|
1339
|
+
done
|
|
1340
|
+
|
|
1341
|
+
# Auto-detect shell if not specified
|
|
1342
|
+
if [[ -z "$target_shell" ]]; then
|
|
1343
|
+
if [[ -n "$BASH_VERSION" ]]; then
|
|
1344
|
+
target_shell="bash"
|
|
1345
|
+
elif [[ -n "$ZSH_VERSION" ]]; then
|
|
1346
|
+
target_shell="zsh"
|
|
1347
|
+
elif [[ -n "$FISH_VERSION" ]]; then
|
|
1348
|
+
target_shell="fish"
|
|
1349
|
+
else
|
|
1350
|
+
echo -e "${RED}[X] Could not detect shell${RESET}" >&2
|
|
1351
|
+
echo "" >&2
|
|
1352
|
+
echo -e "${YELLOW}Usage:${RESET}" >&2
|
|
1353
|
+
echo " ccs --shell-completion # Auto-detect shell" >&2
|
|
1354
|
+
echo " ccs --shell-completion --bash # Install for bash" >&2
|
|
1355
|
+
echo " ccs --shell-completion --zsh # Install for zsh" >&2
|
|
1356
|
+
echo " ccs --shell-completion --fish # Install for fish" >&2
|
|
1357
|
+
echo "" >&2
|
|
1358
|
+
return 1
|
|
1359
|
+
fi
|
|
1360
|
+
fi
|
|
1361
|
+
|
|
1362
|
+
# Ensure completion files exist in ~/.ccs/completions/
|
|
1363
|
+
local completions_dir="$HOME/.ccs/completions"
|
|
1364
|
+
if [[ ! -d "$completions_dir" ]]; then
|
|
1365
|
+
mkdir -p "$completions_dir"
|
|
1366
|
+
fi
|
|
1367
|
+
|
|
1368
|
+
# Copy from scripts if not present
|
|
1369
|
+
local script_dir="$(dirname "$0")"
|
|
1370
|
+
if [[ -f "$script_dir/../scripts/completion/ccs.bash" ]]; then
|
|
1371
|
+
cp "$script_dir/../scripts/completion/ccs.bash" "$completions_dir/" 2>/dev/null || true
|
|
1372
|
+
cp "$script_dir/../scripts/completion/ccs.zsh" "$completions_dir/" 2>/dev/null || true
|
|
1373
|
+
cp "$script_dir/../scripts/completion/ccs.fish" "$completions_dir/" 2>/dev/null || true
|
|
1374
|
+
fi
|
|
1375
|
+
|
|
1376
|
+
# Install based on target shell
|
|
1377
|
+
case "$target_shell" in
|
|
1378
|
+
bash)
|
|
1379
|
+
local rc_file="$HOME/.bashrc"
|
|
1380
|
+
local completion_file="$completions_dir/ccs.bash"
|
|
1381
|
+
local marker="# CCS shell completion"
|
|
1382
|
+
|
|
1383
|
+
if [[ ! -f "$completion_file" ]]; then
|
|
1384
|
+
echo -e "${RED}[X] Completion file not found: $completion_file${RESET}" >&2
|
|
1385
|
+
echo " Please reinstall CCS." >&2
|
|
1386
|
+
return 1
|
|
1387
|
+
fi
|
|
1388
|
+
|
|
1389
|
+
# Check if already installed
|
|
1390
|
+
if grep -q "$marker" "$rc_file" 2>/dev/null; then
|
|
1391
|
+
echo -e "${GREEN}[OK] Shell completion already installed${RESET}"
|
|
1392
|
+
echo ""
|
|
1393
|
+
return 0
|
|
1394
|
+
fi
|
|
1395
|
+
|
|
1396
|
+
# Append to .bashrc
|
|
1397
|
+
{
|
|
1398
|
+
echo ""
|
|
1399
|
+
echo "$marker"
|
|
1400
|
+
echo "source \"$completion_file\""
|
|
1401
|
+
} >> "$rc_file"
|
|
1402
|
+
|
|
1403
|
+
echo -e "${GREEN}[OK] Shell completion installed successfully!${RESET}"
|
|
1404
|
+
echo ""
|
|
1405
|
+
echo "Added to $rc_file"
|
|
1406
|
+
echo ""
|
|
1407
|
+
echo -e "${CYAN}To activate:${RESET}"
|
|
1408
|
+
echo " source ~/.bashrc"
|
|
1409
|
+
echo ""
|
|
1410
|
+
echo -e "${CYAN}Then test:${RESET}"
|
|
1411
|
+
echo " ccs <TAB> # See available profiles"
|
|
1412
|
+
echo " ccs auth <TAB> # See auth subcommands"
|
|
1413
|
+
echo ""
|
|
1414
|
+
;;
|
|
1415
|
+
|
|
1416
|
+
zsh)
|
|
1417
|
+
local rc_file="$HOME/.zshrc"
|
|
1418
|
+
local completion_dir="$HOME/.zsh/completion"
|
|
1419
|
+
local completion_file="$completions_dir/ccs.zsh"
|
|
1420
|
+
local marker="# CCS shell completion"
|
|
1421
|
+
|
|
1422
|
+
if [[ ! -f "$completion_file" ]]; then
|
|
1423
|
+
echo -e "${RED}[X] Completion file not found: $completion_file${RESET}" >&2
|
|
1424
|
+
echo " Please reinstall CCS." >&2
|
|
1425
|
+
return 1
|
|
1426
|
+
fi
|
|
1427
|
+
|
|
1428
|
+
# Create zsh completion directory
|
|
1429
|
+
mkdir -p "$completion_dir"
|
|
1430
|
+
|
|
1431
|
+
# Copy to zsh completion directory
|
|
1432
|
+
cp "$completion_file" "$completion_dir/_ccs"
|
|
1433
|
+
|
|
1434
|
+
# Check if already installed
|
|
1435
|
+
if grep -q "$marker" "$rc_file" 2>/dev/null; then
|
|
1436
|
+
echo -e "${GREEN}[OK] Shell completion already installed${RESET}"
|
|
1437
|
+
echo ""
|
|
1438
|
+
return 0
|
|
1439
|
+
fi
|
|
1440
|
+
|
|
1441
|
+
# Append to .zshrc
|
|
1442
|
+
{
|
|
1443
|
+
echo ""
|
|
1444
|
+
echo "$marker"
|
|
1445
|
+
echo "fpath=(~/.zsh/completion \$fpath)"
|
|
1446
|
+
echo "autoload -Uz compinit && compinit"
|
|
1447
|
+
} >> "$rc_file"
|
|
1448
|
+
|
|
1449
|
+
echo -e "${GREEN}[OK] Shell completion installed successfully!${RESET}"
|
|
1450
|
+
echo ""
|
|
1451
|
+
echo "Added to $rc_file"
|
|
1452
|
+
echo ""
|
|
1453
|
+
echo -e "${CYAN}To activate:${RESET}"
|
|
1454
|
+
echo " source ~/.zshrc"
|
|
1455
|
+
echo ""
|
|
1456
|
+
echo -e "${CYAN}Then test:${RESET}"
|
|
1457
|
+
echo " ccs <TAB> # See available profiles"
|
|
1458
|
+
echo " ccs auth <TAB> # See auth subcommands"
|
|
1459
|
+
echo ""
|
|
1460
|
+
;;
|
|
1461
|
+
|
|
1462
|
+
fish)
|
|
1463
|
+
local fish_dir="$HOME/.config/fish/completions"
|
|
1464
|
+
local completion_file="$completions_dir/ccs.fish"
|
|
1465
|
+
|
|
1466
|
+
if [[ ! -f "$completion_file" ]]; then
|
|
1467
|
+
echo -e "${RED}[X] Completion file not found: $completion_file${RESET}" >&2
|
|
1468
|
+
echo " Please reinstall CCS." >&2
|
|
1469
|
+
return 1
|
|
1470
|
+
fi
|
|
1471
|
+
|
|
1472
|
+
# Create fish completion directory
|
|
1473
|
+
mkdir -p "$fish_dir"
|
|
1474
|
+
|
|
1475
|
+
# Copy to fish completion directory (fish auto-loads from here)
|
|
1476
|
+
cp "$completion_file" "$fish_dir/ccs.fish"
|
|
1477
|
+
|
|
1478
|
+
echo -e "${GREEN}[OK] Shell completion installed successfully!${RESET}"
|
|
1479
|
+
echo ""
|
|
1480
|
+
echo "Installed to $fish_dir/ccs.fish"
|
|
1481
|
+
echo ""
|
|
1482
|
+
echo -e "${CYAN}To activate:${RESET}"
|
|
1483
|
+
echo " Fish auto-loads completions (no reload needed)"
|
|
1484
|
+
echo ""
|
|
1485
|
+
echo -e "${CYAN}Then test:${RESET}"
|
|
1486
|
+
echo " ccs <TAB> # See available profiles"
|
|
1487
|
+
echo " ccs auth <TAB> # See auth subcommands"
|
|
1488
|
+
echo ""
|
|
1489
|
+
;;
|
|
1490
|
+
|
|
1491
|
+
*)
|
|
1492
|
+
echo -e "${RED}[X] Unsupported shell: $target_shell${RESET}" >&2
|
|
1493
|
+
return 1
|
|
1494
|
+
;;
|
|
1495
|
+
esac
|
|
1496
|
+
|
|
1497
|
+
return 0
|
|
1498
|
+
}
|
|
1499
|
+
|
|
982
1500
|
# --- Main Execution Logic ---
|
|
983
1501
|
|
|
984
1502
|
# Special case: version command (check BEFORE profile detection)
|
|
@@ -999,6 +1517,12 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "auth" ]]; then
|
|
|
999
1517
|
exit $?
|
|
1000
1518
|
fi
|
|
1001
1519
|
|
|
1520
|
+
# Special case: shell completion installer
|
|
1521
|
+
if [[ $# -gt 0 ]] && [[ "${1}" == "--shell-completion" ]]; then
|
|
1522
|
+
install_shell_completion "$@"
|
|
1523
|
+
exit $?
|
|
1524
|
+
fi
|
|
1525
|
+
|
|
1002
1526
|
# Special case: doctor command
|
|
1003
1527
|
if [[ $# -gt 0 ]] && [[ "${1}" == "doctor" || "${1}" == "--doctor" ]]; then
|
|
1004
1528
|
doctor_run
|
|
@@ -1030,10 +1554,36 @@ fi
|
|
|
1030
1554
|
|
|
1031
1555
|
# Detect profile type
|
|
1032
1556
|
if ! detect_profile_type "$PROFILE"; then
|
|
1033
|
-
|
|
1557
|
+
# Get suggestions using fuzzy matching
|
|
1558
|
+
mapfile -t all_profiles < <(get_all_profile_names)
|
|
1559
|
+
mapfile -t suggestions < <(find_similar_strings "$PROFILE" "${all_profiles[@]}")
|
|
1560
|
+
|
|
1561
|
+
echo "" >&2
|
|
1562
|
+
echo -e "${RED}[X] Profile '$PROFILE' not found${RESET}" >&2
|
|
1563
|
+
echo "" >&2
|
|
1034
1564
|
|
|
1035
|
-
|
|
1036
|
-
$
|
|
1565
|
+
# Show suggestions if any
|
|
1566
|
+
if [[ ${#suggestions[@]} -gt 0 ]]; then
|
|
1567
|
+
echo -e "${YELLOW}Did you mean:${RESET}" >&2
|
|
1568
|
+
for suggestion in "${suggestions[@]}"; do
|
|
1569
|
+
echo " $suggestion" >&2
|
|
1570
|
+
done
|
|
1571
|
+
echo "" >&2
|
|
1572
|
+
fi
|
|
1573
|
+
|
|
1574
|
+
echo -e "${CYAN}Available profiles:${RESET}" >&2
|
|
1575
|
+
list_available_profiles >&2
|
|
1576
|
+
echo "" >&2
|
|
1577
|
+
echo -e "${YELLOW}Solutions:${RESET}" >&2
|
|
1578
|
+
echo " # Use existing profile" >&2
|
|
1579
|
+
echo " ccs <profile> \"your prompt\"" >&2
|
|
1580
|
+
echo "" >&2
|
|
1581
|
+
echo " # Create new account profile" >&2
|
|
1582
|
+
echo " ccs auth create <name>" >&2
|
|
1583
|
+
echo "" >&2
|
|
1584
|
+
echo -e "${YELLOW}Error: $E_PROFILE_NOT_FOUND${RESET}" >&2
|
|
1585
|
+
echo -e "${YELLOW}$(get_error_doc_url "$E_PROFILE_NOT_FOUND")${RESET}" >&2
|
|
1586
|
+
echo "" >&2
|
|
1037
1587
|
exit 1
|
|
1038
1588
|
fi
|
|
1039
1589
|
|