@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 CHANGED
@@ -2,12 +2,32 @@
2
2
  set -euo pipefail
3
3
 
4
4
  # Version (updated by scripts/bump-version.sh)
5
- CCS_VERSION="3.4.6"
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}╔═════════════════════════════════════════════╗${RESET}" >&2
31
- echo -e "${RED}${BOLD} ERROR ║${RESET}" >&2
32
- echo -e "${RED}${BOLD}╚═════════════════════════════════════════════╝${RESET}" >&2
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
- [[ "${1:-}" == "--verbose" ]] && verbose=true
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="${1:-}"
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>${RESET}"
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 // \"Never\"" "$PROFILES_JSON" 2>/dev/null || true)
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
- --force) force=true ;;
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 "Usage: ${YELLOW}ccs auth remove <profile> --force${RESET}"
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
- $force || {
925
- msg_error "Removal requires --force flag for safety"
926
- echo "Run: ${YELLOW}ccs auth remove $profile_name --force${RESET}"
927
- return 1
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
- msg_error "Profile '$PROFILE' not found
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
- Available profiles:
1036
- $(list_available_profiles)"
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