@kaitranntt/ccs 4.1.3 → 4.1.5
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/VERSION +1 -1
- package/bin/auth/auth-commands.js +1 -1
- package/bin/ccs.js +69 -54
- package/bin/utils/claude-symlink-manager.js +7 -7
- package/bin/utils/shell-completion.js +34 -12
- package/lib/ccs +203 -63
- package/lib/ccs.ps1 +237 -49
- package/package.json +1 -1
- package/scripts/completion/ccs.bash +8 -2
- package/scripts/completion/ccs.fish +49 -13
- package/scripts/completion/ccs.ps1 +17 -1
- package/scripts/completion/ccs.zsh +52 -10
package/lib/ccs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
# Version (updated by scripts/bump-version.sh)
|
|
5
|
-
CCS_VERSION="4.1.
|
|
5
|
+
CCS_VERSION="4.1.5"
|
|
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"
|
|
@@ -165,74 +165,82 @@ find_similar_strings() {
|
|
|
165
165
|
show_help() {
|
|
166
166
|
echo -e "${BOLD}CCS (Claude Code Switch) - Instant profile switching for Claude CLI${RESET}"
|
|
167
167
|
echo ""
|
|
168
|
+
|
|
168
169
|
echo -e "${CYAN}Usage:${RESET}"
|
|
169
170
|
echo -e " ${YELLOW}ccs${RESET} [profile] [claude-args...]"
|
|
170
|
-
echo -e " ${YELLOW}ccs auth${RESET} <command> [options]"
|
|
171
171
|
echo -e " ${YELLOW}ccs${RESET} [flags]"
|
|
172
172
|
echo ""
|
|
173
|
+
|
|
173
174
|
echo -e "${CYAN}Description:${RESET}"
|
|
174
|
-
echo -e " Switch between multiple Claude accounts
|
|
175
|
-
echo -e "
|
|
176
|
-
echo -e " auto-recovery. Zero downtime."
|
|
175
|
+
echo -e " Switch between multiple Claude accounts and alternative models"
|
|
176
|
+
echo -e " (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently"
|
|
177
|
+
echo -e " with auto-recovery. Zero downtime."
|
|
177
178
|
echo ""
|
|
179
|
+
|
|
178
180
|
echo -e "${CYAN}Model Switching:${RESET}"
|
|
179
181
|
echo -e " ${YELLOW}ccs${RESET} Use default Claude account"
|
|
180
182
|
echo -e " ${YELLOW}ccs glm${RESET} Switch to GLM 4.6 model"
|
|
181
183
|
echo -e " ${YELLOW}ccs glmt${RESET} Switch to GLM with thinking mode"
|
|
184
|
+
echo -e " ${YELLOW}ccs glmt --verbose${RESET} Enable debug logging"
|
|
182
185
|
echo -e " ${YELLOW}ccs kimi${RESET} Switch to Kimi for Coding"
|
|
183
186
|
echo -e " ${YELLOW}ccs glm${RESET} \"debug this code\" Use GLM and run command"
|
|
184
187
|
echo ""
|
|
188
|
+
|
|
185
189
|
echo -e "${CYAN}Account Management:${RESET}"
|
|
186
|
-
echo -e " ${YELLOW}ccs auth --help${RESET}
|
|
187
|
-
echo -e " ${YELLOW}ccs work${RESET} Switch to work account"
|
|
188
|
-
echo -e " ${YELLOW}ccs personal${RESET} Switch to personal account"
|
|
190
|
+
echo -e " ${YELLOW}ccs auth --help${RESET} Run multiple Claude accounts concurrently"
|
|
189
191
|
echo ""
|
|
190
|
-
|
|
191
|
-
echo -e "
|
|
192
|
+
|
|
193
|
+
echo -e "${CYAN}Delegation (inside Claude Code CLI):${RESET}"
|
|
194
|
+
echo -e " ${YELLOW}/ccs:glm \"task\"${RESET} Delegate to GLM-4.6 for simple tasks"
|
|
192
195
|
echo -e " ${YELLOW}/ccs:kimi \"task\"${RESET} Delegate to Kimi for long context"
|
|
193
|
-
echo -e "
|
|
194
|
-
echo -e " Use delegation to save tokens on simple tasks"
|
|
195
|
-
echo -e " Commands work inside Claude Code sessions only"
|
|
196
|
+
echo -e " Save tokens by delegating simple tasks to cost-optimized models"
|
|
196
197
|
echo ""
|
|
198
|
+
|
|
197
199
|
echo -e "${CYAN}Diagnostics:${RESET}"
|
|
198
200
|
echo -e " ${YELLOW}ccs doctor${RESET} Run health check and diagnostics"
|
|
201
|
+
echo -e " ${YELLOW}ccs sync${RESET} Sync delegation commands and skills"
|
|
199
202
|
echo ""
|
|
203
|
+
|
|
200
204
|
echo -e "${CYAN}Flags:${RESET}"
|
|
201
205
|
echo -e " ${YELLOW}-h, --help${RESET} Show this help message"
|
|
202
206
|
echo -e " ${YELLOW}-v, --version${RESET} Show version and installation info"
|
|
203
|
-
echo -e " ${YELLOW}--shell-completion${RESET}
|
|
207
|
+
echo -e " ${YELLOW}-sc, --shell-completion${RESET} Install shell auto-completion"
|
|
204
208
|
echo ""
|
|
209
|
+
|
|
205
210
|
echo -e "${CYAN}Configuration:${RESET}"
|
|
206
|
-
echo -e " Config:
|
|
207
|
-
echo -e " Profiles:
|
|
208
|
-
echo -e " Instances:
|
|
209
|
-
echo -e " Settings:
|
|
211
|
+
echo -e " Config File: ~/.ccs/config.json"
|
|
212
|
+
echo -e " Profiles: ~/.ccs/profiles.json"
|
|
213
|
+
echo -e " Instances: ~/.ccs/instances/"
|
|
214
|
+
echo -e " Settings: ~/.ccs/*.settings.json"
|
|
215
|
+
echo -e " Environment: CCS_CONFIG (override config path)"
|
|
210
216
|
echo ""
|
|
217
|
+
|
|
211
218
|
echo -e "${CYAN}Shared Data:${RESET}"
|
|
212
|
-
echo -e " Commands:
|
|
213
|
-
echo -e " Skills:
|
|
219
|
+
echo -e " Commands: ~/.ccs/shared/commands/"
|
|
220
|
+
echo -e " Skills: ~/.ccs/shared/skills/"
|
|
221
|
+
echo -e " Agents: ~/.ccs/shared/agents/"
|
|
214
222
|
echo -e " Note: Commands, skills, and agents are symlinked across all profiles"
|
|
215
223
|
echo ""
|
|
224
|
+
|
|
216
225
|
echo -e "${CYAN}Examples:${RESET}"
|
|
217
|
-
echo -e "
|
|
218
|
-
echo -e "
|
|
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"
|
|
226
|
+
echo -e " ${YELLOW}\$ ccs${RESET} # Use default account"
|
|
227
|
+
echo -e " ${YELLOW}\$ ccs glm \"implement API\"${RESET} # Cost-optimized model"
|
|
224
228
|
echo ""
|
|
225
|
-
echo -e " For more: ${CYAN}https://github.com/kaitranntt/ccs
|
|
229
|
+
echo -e " For more: ${CYAN}https://github.com/kaitranntt/ccs/blob/main/README.md${RESET}"
|
|
226
230
|
echo ""
|
|
231
|
+
|
|
227
232
|
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"
|
|
233
|
+
echo -e " npm: npm uninstall -g @kaitranntt/ccs"
|
|
234
|
+
echo -e " macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash"
|
|
235
|
+
echo -e " Windows: irm ccs.kaitran.ca/uninstall | iex"
|
|
231
236
|
echo ""
|
|
237
|
+
|
|
232
238
|
echo -e "${CYAN}Documentation:${RESET}"
|
|
233
239
|
echo -e " GitHub: ${CYAN}https://github.com/kaitranntt/ccs${RESET}"
|
|
234
240
|
echo -e " Docs: https://github.com/kaitranntt/ccs/blob/main/README.md"
|
|
241
|
+
echo -e " Issues: https://github.com/kaitranntt/ccs/issues"
|
|
235
242
|
echo ""
|
|
243
|
+
|
|
236
244
|
echo -e "${CYAN}License:${RESET} MIT"
|
|
237
245
|
}
|
|
238
246
|
|
|
@@ -489,6 +497,100 @@ doctor_run() {
|
|
|
489
497
|
$has_errors && exit 1 || exit 0
|
|
490
498
|
}
|
|
491
499
|
|
|
500
|
+
# --- Sync Command ---
|
|
501
|
+
|
|
502
|
+
sync_run() {
|
|
503
|
+
local ccs_claude_dir="$HOME/.ccs/.claude"
|
|
504
|
+
local user_claude_dir="$HOME/.claude"
|
|
505
|
+
|
|
506
|
+
echo -e "${CYAN}Syncing delegation commands and skills to ~/.claude/...${RESET}"
|
|
507
|
+
echo ""
|
|
508
|
+
|
|
509
|
+
# Check if source directory exists
|
|
510
|
+
if [[ ! -d "$ccs_claude_dir" ]]; then
|
|
511
|
+
msg_error "CCS .claude/ directory not found at $ccs_claude_dir"
|
|
512
|
+
echo "Reinstall CCS: npm install -g @kaitranntt/ccs --force"
|
|
513
|
+
exit 1
|
|
514
|
+
fi
|
|
515
|
+
|
|
516
|
+
# Create ~/.claude/ if missing
|
|
517
|
+
if [[ ! -d "$user_claude_dir" ]]; then
|
|
518
|
+
echo -e "${CYAN}[i]${RESET} Creating ~/.claude/ directory"
|
|
519
|
+
mkdir -p "$user_claude_dir"
|
|
520
|
+
chmod 700 "$user_claude_dir"
|
|
521
|
+
fi
|
|
522
|
+
|
|
523
|
+
# Items to symlink (source:target:type)
|
|
524
|
+
local items=(
|
|
525
|
+
"commands/ccs:commands/ccs:dir"
|
|
526
|
+
"skills/ccs-delegation:skills/ccs-delegation:dir"
|
|
527
|
+
"agents/ccs-delegator.md:agents/ccs-delegator.md:file"
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
local installed=0
|
|
531
|
+
local skipped=0
|
|
532
|
+
|
|
533
|
+
for item in "${items[@]}"; do
|
|
534
|
+
IFS=':' read -r source target type <<< "$item"
|
|
535
|
+
local source_path="$ccs_claude_dir/$source"
|
|
536
|
+
local target_path="$user_claude_dir/$target"
|
|
537
|
+
local target_dir="$(dirname "$target_path")"
|
|
538
|
+
|
|
539
|
+
# Check source exists
|
|
540
|
+
if [[ ! -e "$source_path" ]]; then
|
|
541
|
+
echo -e "${YELLOW}[!]${RESET} Source not found: $source, skipping"
|
|
542
|
+
continue
|
|
543
|
+
fi
|
|
544
|
+
|
|
545
|
+
# Create parent directory if needed
|
|
546
|
+
if [[ ! -d "$target_dir" ]]; then
|
|
547
|
+
mkdir -p "$target_dir"
|
|
548
|
+
chmod 700 "$target_dir"
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
# Check if already correct symlink
|
|
552
|
+
if [[ -L "$target_path" ]]; then
|
|
553
|
+
local link_target="$(readlink "$target_path")"
|
|
554
|
+
local resolved_target="$(cd "$(dirname "$target_path")" && cd "$(dirname "$link_target")" && pwd)/$(basename "$link_target")"
|
|
555
|
+
|
|
556
|
+
if [[ "$resolved_target" == "$source_path" ]]; then
|
|
557
|
+
((skipped++))
|
|
558
|
+
continue
|
|
559
|
+
fi
|
|
560
|
+
fi
|
|
561
|
+
|
|
562
|
+
# Backup existing file/directory
|
|
563
|
+
if [[ -e "$target_path" ]]; then
|
|
564
|
+
local timestamp="$(date +%Y-%m-%d)"
|
|
565
|
+
local backup_path="${target_path}.backup-${timestamp}"
|
|
566
|
+
local counter=1
|
|
567
|
+
|
|
568
|
+
while [[ -e "$backup_path" ]]; do
|
|
569
|
+
backup_path="${target_path}.backup-${timestamp}-${counter}"
|
|
570
|
+
((counter++))
|
|
571
|
+
done
|
|
572
|
+
|
|
573
|
+
mv "$target_path" "$backup_path"
|
|
574
|
+
echo -e "${CYAN}[i]${RESET} Backed up existing to $(basename "$backup_path")"
|
|
575
|
+
fi
|
|
576
|
+
|
|
577
|
+
# Create symlink
|
|
578
|
+
ln -s "$source_path" "$target_path" 2>/dev/null
|
|
579
|
+
if [[ $? -eq 0 ]]; then
|
|
580
|
+
echo -e "${GREEN}[OK]${RESET} Installed $target"
|
|
581
|
+
((installed++))
|
|
582
|
+
else
|
|
583
|
+
echo -e "${RED}[X]${RESET} Failed to install $target"
|
|
584
|
+
fi
|
|
585
|
+
done
|
|
586
|
+
|
|
587
|
+
echo ""
|
|
588
|
+
echo -e "${GREEN}✓ Update complete${RESET}"
|
|
589
|
+
echo " Installed: $installed"
|
|
590
|
+
echo " Already up-to-date: $skipped"
|
|
591
|
+
echo ""
|
|
592
|
+
}
|
|
593
|
+
|
|
492
594
|
# --- Claude CLI Detection Logic ---
|
|
493
595
|
|
|
494
596
|
detect_claude_cli() {
|
|
@@ -518,49 +620,81 @@ Restart your terminal after installation."
|
|
|
518
620
|
}
|
|
519
621
|
|
|
520
622
|
show_version() {
|
|
623
|
+
# Title
|
|
521
624
|
echo -e "${BOLD}CCS (Claude Code Switch) v${CCS_VERSION}${RESET}"
|
|
522
625
|
echo ""
|
|
626
|
+
|
|
627
|
+
# Installation section with table-like formatting
|
|
523
628
|
echo -e "${CYAN}Installation:${RESET}"
|
|
524
629
|
|
|
525
|
-
#
|
|
526
|
-
local
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
# Simple config display
|
|
530
|
-
local config="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
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
|
|
630
|
+
# Location - prioritize script location over command location
|
|
631
|
+
local script_location="$(readlink -f "${BASH_SOURCE[0]}")"
|
|
632
|
+
local command_location=$(command -v ccs 2>/dev/null || echo "(not found)")
|
|
550
633
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
634
|
+
# Show script location if running from source
|
|
635
|
+
if [[ "$script_location" == *"ccs"* ]]; then
|
|
636
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Location:" "${script_location}"
|
|
637
|
+
else
|
|
638
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Location:" "${command_location}"
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
# .ccs/ directory location
|
|
642
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "CCS Directory:" "$HOME/.ccs/"
|
|
643
|
+
|
|
644
|
+
# Config path
|
|
645
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Config:" "${CONFIG_FILE}"
|
|
646
|
+
|
|
647
|
+
# Profiles.json location
|
|
648
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Profiles:" "${PROFILES_JSON}"
|
|
649
|
+
|
|
650
|
+
# Delegation status - check multiple indicators
|
|
651
|
+
local delegation_configured=false
|
|
652
|
+
local ready_profiles=()
|
|
653
|
+
|
|
654
|
+
# Check for delegation-sessions.json (primary indicator)
|
|
655
|
+
local delegation_sessions="$HOME/.ccs/delegation-sessions.json"
|
|
656
|
+
if [[ -f "$delegation_sessions" ]]; then
|
|
657
|
+
delegation_configured=true
|
|
658
|
+
fi
|
|
659
|
+
|
|
660
|
+
# Check for profiles with valid API keys (secondary indicator)
|
|
661
|
+
for profile in glm kimi; do
|
|
662
|
+
local settings_file="$HOME/.ccs/$profile.settings.json"
|
|
663
|
+
if [[ -f "$settings_file" ]]; then
|
|
664
|
+
# Check if API key is configured (not a placeholder)
|
|
665
|
+
local api_key=$(jq -r '.env.ANTHROPIC_AUTH_TOKEN // empty' "$settings_file" 2>/dev/null)
|
|
666
|
+
if [[ -n "$api_key" ]] && [[ ! "$api_key" =~ YOUR_.*_API_KEY_HERE ]] && [[ ! "$api_key" =~ sk-test.* ]]; then
|
|
667
|
+
ready_profiles+=("$profile")
|
|
668
|
+
delegation_configured=true
|
|
669
|
+
fi
|
|
555
670
|
fi
|
|
671
|
+
done
|
|
672
|
+
|
|
673
|
+
if $delegation_configured; then
|
|
674
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Delegation:" "Enabled"
|
|
556
675
|
else
|
|
557
|
-
|
|
676
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Delegation:" "Not configured"
|
|
558
677
|
fi
|
|
678
|
+
|
|
559
679
|
echo ""
|
|
560
680
|
|
|
681
|
+
# Ready Profiles section - make it more prominent
|
|
682
|
+
if [[ ${#ready_profiles[@]} -gt 0 ]]; then
|
|
683
|
+
echo -e "${CYAN}Delegation Ready:${RESET}"
|
|
684
|
+
echo " ${YELLOW}✓${RESET} ${ready_profiles[*]} profiles are ready for delegation"
|
|
685
|
+
echo ""
|
|
686
|
+
elif $delegation_configured; then
|
|
687
|
+
echo -e "${CYAN}Delegation Ready:${RESET}"
|
|
688
|
+
echo " ${YELLOW}!${RESET} Delegation configured but no valid API keys found"
|
|
689
|
+
echo ""
|
|
690
|
+
fi
|
|
691
|
+
|
|
692
|
+
# Documentation
|
|
561
693
|
echo -e "${CYAN}Documentation:${RESET} https://github.com/kaitranntt/ccs"
|
|
562
694
|
echo -e "${CYAN}License:${RESET} MIT"
|
|
563
695
|
echo ""
|
|
696
|
+
|
|
697
|
+
# Help hint
|
|
564
698
|
echo -e "${YELLOW}Run 'ccs --help' for usage information${RESET}"
|
|
565
699
|
}
|
|
566
700
|
|
|
@@ -966,7 +1100,7 @@ detect_profile_type() {
|
|
|
966
1100
|
# --- Auth Commands (Phase 3) ---
|
|
967
1101
|
|
|
968
1102
|
auth_help() {
|
|
969
|
-
echo -e "${BOLD}CCS Account Management${RESET}"
|
|
1103
|
+
echo -e "${BOLD}CCS Concurrent Account Management${RESET}"
|
|
970
1104
|
echo ""
|
|
971
1105
|
echo -e "${CYAN}Usage:${RESET}"
|
|
972
1106
|
echo -e " ${YELLOW}ccs auth${RESET} <command> [options]"
|
|
@@ -1518,7 +1652,7 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "auth" ]]; then
|
|
|
1518
1652
|
fi
|
|
1519
1653
|
|
|
1520
1654
|
# Special case: shell completion installer
|
|
1521
|
-
if [[ $# -gt 0 ]] && [[ "${1}" == "--shell-completion" ]]; then
|
|
1655
|
+
if [[ $# -gt 0 ]] && [[ "${1}" == "--shell-completion" || "${1}" == "-sc" ]]; then
|
|
1522
1656
|
install_shell_completion "$@"
|
|
1523
1657
|
exit $?
|
|
1524
1658
|
fi
|
|
@@ -1529,6 +1663,12 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "doctor" || "${1}" == "--doctor" ]]; then
|
|
|
1529
1663
|
exit $?
|
|
1530
1664
|
fi
|
|
1531
1665
|
|
|
1666
|
+
# Special case: sync command
|
|
1667
|
+
if [[ $# -gt 0 ]] && [[ "${1}" == "sync" || "${1}" == "--sync" ]]; then
|
|
1668
|
+
sync_run
|
|
1669
|
+
exit $?
|
|
1670
|
+
fi
|
|
1671
|
+
|
|
1532
1672
|
# Run auto-recovery before main logic
|
|
1533
1673
|
auto_recover || {
|
|
1534
1674
|
msg_error "Auto-recovery failed. Check permissions."
|