@kaitranntt/ccs 4.1.4 → 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 +60 -36
- package/bin/utils/claude-symlink-manager.js +7 -7
- package/lib/ccs +195 -46
- package/lib/ccs.ps1 +229 -37
- package/package.json +1 -1
- package/scripts/completion/ccs.bash +2 -2
- package/scripts/completion/ccs.fish +14 -13
- package/scripts/completion/ccs.ps1 +2 -2
- package/scripts/completion/ccs.zsh +3 -2
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.1.
|
|
1
|
+
4.1.5
|
|
@@ -29,7 +29,7 @@ class AuthCommands {
|
|
|
29
29
|
* Show help for auth commands
|
|
30
30
|
*/
|
|
31
31
|
showHelp() {
|
|
32
|
-
console.log(colored('CCS Account Management', 'bold'));
|
|
32
|
+
console.log(colored('CCS Concurrent Account Management', 'bold'));
|
|
33
33
|
console.log('');
|
|
34
34
|
console.log(colored('Usage:', 'cyan'));
|
|
35
35
|
console.log(` ${colored('ccs auth', 'yellow')} <command> [options]`);
|
package/bin/ccs.js
CHANGED
|
@@ -63,45 +63,69 @@ function handleVersionCommand() {
|
|
|
63
63
|
console.log(colored(`CCS (Claude Code Switch) v${CCS_VERSION}`, 'bold'));
|
|
64
64
|
console.log('');
|
|
65
65
|
|
|
66
|
-
// Installation section
|
|
66
|
+
// Installation section with table-like formatting
|
|
67
67
|
console.log(colored('Installation:', 'cyan'));
|
|
68
68
|
|
|
69
69
|
// Location
|
|
70
70
|
const installLocation = process.argv[1] || '(not found)';
|
|
71
|
-
console.log(` ${colored('Location:', 'cyan')} ${installLocation}`);
|
|
71
|
+
console.log(` ${colored('Location:'.padEnd(17), 'cyan')} ${installLocation}`);
|
|
72
|
+
|
|
73
|
+
// .ccs/ directory location
|
|
74
|
+
const ccsDir = path.join(os.homedir(), '.ccs');
|
|
75
|
+
console.log(` ${colored('CCS Directory:'.padEnd(17), 'cyan')} ${ccsDir}`);
|
|
72
76
|
|
|
73
77
|
// Config path
|
|
74
78
|
const configPath = getConfigPath();
|
|
75
|
-
console.log(` ${colored('Config:', 'cyan')} ${configPath}`);
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
console.log(` ${colored('Config:'.padEnd(17), 'cyan')} ${configPath}`);
|
|
80
|
+
|
|
81
|
+
// Profiles.json location
|
|
82
|
+
const profilesJson = path.join(os.homedir(), '.ccs', 'profiles.json');
|
|
83
|
+
console.log(` ${colored('Profiles:'.padEnd(17), 'cyan')} ${profilesJson}`);
|
|
84
|
+
|
|
85
|
+
// Delegation status - check multiple indicators
|
|
86
|
+
const delegationSessionsPath = path.join(os.homedir(), '.ccs', 'delegation-sessions.json');
|
|
87
|
+
const delegationConfigured = fs.existsSync(delegationSessionsPath);
|
|
88
|
+
|
|
89
|
+
let readyProfiles = [];
|
|
90
|
+
|
|
91
|
+
// Check for profiles with valid API keys
|
|
92
|
+
for (const profile of ['glm', 'kimi']) {
|
|
93
|
+
const settingsPath = path.join(os.homedir(), '.ccs', `${profile}.settings.json`);
|
|
94
|
+
if (fs.existsSync(settingsPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
97
|
+
const apiKey = settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
98
|
+
if (apiKey && !apiKey.match(/YOUR_.*_API_KEY_HERE/) && !apiKey.match(/sk-test.*/)) {
|
|
99
|
+
readyProfiles.push(profile);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Invalid JSON, skip
|
|
92
103
|
}
|
|
93
104
|
}
|
|
105
|
+
}
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
107
|
+
const hasValidApiKeys = readyProfiles.length > 0;
|
|
108
|
+
const delegationEnabled = delegationConfigured || hasValidApiKeys;
|
|
109
|
+
|
|
110
|
+
if (delegationEnabled) {
|
|
111
|
+
console.log(` ${colored('Delegation:'.padEnd(17), 'cyan')} Enabled`);
|
|
100
112
|
} else {
|
|
101
|
-
console.log(` ${colored('Delegation:', 'cyan')} Not configured`);
|
|
113
|
+
console.log(` ${colored('Delegation:'.padEnd(17), 'cyan')} Not configured`);
|
|
102
114
|
}
|
|
115
|
+
|
|
103
116
|
console.log('');
|
|
104
117
|
|
|
118
|
+
// Ready Profiles section - make it more prominent
|
|
119
|
+
if (readyProfiles.length > 0) {
|
|
120
|
+
console.log(colored('Delegation Ready:', 'cyan'));
|
|
121
|
+
console.log(` ${colored('✓', 'yellow')} ${readyProfiles.join(', ')} profiles are ready for delegation`);
|
|
122
|
+
console.log('');
|
|
123
|
+
} else if (delegationEnabled) {
|
|
124
|
+
console.log(colored('Delegation Ready:', 'cyan'));
|
|
125
|
+
console.log(` ${colored('!', 'yellow')} Delegation configured but no valid API keys found`);
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
|
|
105
129
|
// Documentation
|
|
106
130
|
console.log(`${colored('Documentation:', 'cyan')} https://github.com/kaitranntt/ccs`);
|
|
107
131
|
console.log(`${colored('License:', 'cyan')} MIT`);
|
|
@@ -143,7 +167,7 @@ function handleHelpCommand() {
|
|
|
143
167
|
|
|
144
168
|
// Account Management
|
|
145
169
|
console.log(colored('Account Management:', 'cyan'));
|
|
146
|
-
console.log(` ${colored('ccs auth --help', 'yellow')}
|
|
170
|
+
console.log(` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently`);
|
|
147
171
|
console.log('');
|
|
148
172
|
|
|
149
173
|
// Delegation (inside Claude Code CLI)
|
|
@@ -156,14 +180,14 @@ function handleHelpCommand() {
|
|
|
156
180
|
// Diagnostics
|
|
157
181
|
console.log(colored('Diagnostics:', 'cyan'));
|
|
158
182
|
console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`);
|
|
159
|
-
console.log(` ${colored('ccs
|
|
183
|
+
console.log(` ${colored('ccs sync', 'yellow')} Sync delegation commands and skills`);
|
|
160
184
|
console.log('');
|
|
161
185
|
|
|
162
186
|
// Flags
|
|
163
187
|
console.log(colored('Flags:', 'cyan'));
|
|
164
188
|
console.log(` ${colored('-h, --help', 'yellow')} Show this help message`);
|
|
165
189
|
console.log(` ${colored('-v, --version', 'yellow')} Show version and installation info`);
|
|
166
|
-
console.log(` ${colored('--shell-completion', 'yellow')}
|
|
190
|
+
console.log(` ${colored('-sc, --shell-completion', 'yellow')} Install shell auto-completion`);
|
|
167
191
|
console.log('');
|
|
168
192
|
|
|
169
193
|
// Configuration
|
|
@@ -188,7 +212,7 @@ function handleHelpCommand() {
|
|
|
188
212
|
console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
|
|
189
213
|
console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
|
|
190
214
|
console.log('');
|
|
191
|
-
console.log(` For more: ${colored('https://github.com/kaitranntt/ccs
|
|
215
|
+
console.log(` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}`);
|
|
192
216
|
console.log('');
|
|
193
217
|
|
|
194
218
|
// Uninstall
|
|
@@ -245,7 +269,7 @@ async function handleDoctorCommand() {
|
|
|
245
269
|
process.exit(doctor.results.isHealthy() ? 0 : 1);
|
|
246
270
|
}
|
|
247
271
|
|
|
248
|
-
async function
|
|
272
|
+
async function handleSyncCommand() {
|
|
249
273
|
// First, copy .claude/ directory from package to ~/.ccs/.claude/
|
|
250
274
|
const ClaudeDirInstaller = require('./utils/claude-dir-installer');
|
|
251
275
|
const installer = new ClaudeDirInstaller();
|
|
@@ -255,8 +279,8 @@ async function handleUpdateCommand() {
|
|
|
255
279
|
const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
|
|
256
280
|
const manager = new ClaudeSymlinkManager();
|
|
257
281
|
|
|
258
|
-
console.log('[i]
|
|
259
|
-
manager.
|
|
282
|
+
console.log('[i] Syncing delegation commands and skills to ~/.claude/...');
|
|
283
|
+
manager.sync();
|
|
260
284
|
|
|
261
285
|
process.exit(0);
|
|
262
286
|
}
|
|
@@ -487,7 +511,7 @@ async function main() {
|
|
|
487
511
|
}
|
|
488
512
|
|
|
489
513
|
// Special case: shell completion installer
|
|
490
|
-
if (firstArg === '--shell-completion') {
|
|
514
|
+
if (firstArg === '--shell-completion' || firstArg === '-sc') {
|
|
491
515
|
await handleShellCompletionCommand(args.slice(1));
|
|
492
516
|
return;
|
|
493
517
|
}
|
|
@@ -498,9 +522,9 @@ async function main() {
|
|
|
498
522
|
return;
|
|
499
523
|
}
|
|
500
524
|
|
|
501
|
-
// Special case:
|
|
502
|
-
if (firstArg === '
|
|
503
|
-
await
|
|
525
|
+
// Special case: sync command (sync delegation commands and skills to ~/.claude/)
|
|
526
|
+
if (firstArg === 'sync' || firstArg === '--sync') {
|
|
527
|
+
await handleSyncCommand();
|
|
504
528
|
return;
|
|
505
529
|
}
|
|
506
530
|
|
|
@@ -53,7 +53,7 @@ class ClaudeSymlinkManager {
|
|
|
53
53
|
this._installItem(item);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
console.log('[OK]
|
|
56
|
+
console.log('[OK] Delegation commands and skills installed to ~/.claude/');
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
@@ -178,9 +178,9 @@ class ClaudeSymlinkManager {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
if (removed > 0) {
|
|
181
|
-
console.log(`[OK] Removed ${removed}
|
|
181
|
+
console.log(`[OK] Removed ${removed} delegation commands and skills from ~/.claude/`);
|
|
182
182
|
} else {
|
|
183
|
-
console.log('[i] No
|
|
183
|
+
console.log('[i] No delegation commands or skills to remove');
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -226,11 +226,11 @@ class ClaudeSymlinkManager {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/**
|
|
229
|
-
*
|
|
230
|
-
* Same as install() but with explicit
|
|
229
|
+
* Sync delegation commands and skills to ~/.claude/ (used by 'ccs sync' command)
|
|
230
|
+
* Same as install() but with explicit sync message
|
|
231
231
|
*/
|
|
232
|
-
|
|
233
|
-
console.log('[i]
|
|
232
|
+
sync() {
|
|
233
|
+
console.log('[i] Syncing delegation commands and skills to ~/.claude/...');
|
|
234
234
|
this.install();
|
|
235
235
|
}
|
|
236
236
|
}
|
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,65 +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
175
|
echo -e " Switch between multiple Claude accounts and alternative models"
|
|
175
176
|
echo -e " (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently"
|
|
176
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}
|
|
190
|
+
echo -e " ${YELLOW}ccs auth --help${RESET} Run multiple Claude accounts concurrently"
|
|
187
191
|
echo ""
|
|
192
|
+
|
|
188
193
|
echo -e "${CYAN}Delegation (inside Claude Code CLI):${RESET}"
|
|
189
194
|
echo -e " ${YELLOW}/ccs:glm \"task\"${RESET} Delegate to GLM-4.6 for simple tasks"
|
|
190
195
|
echo -e " ${YELLOW}/ccs:kimi \"task\"${RESET} Delegate to Kimi for long context"
|
|
191
196
|
echo -e " Save tokens by delegating simple tasks to cost-optimized models"
|
|
192
197
|
echo ""
|
|
198
|
+
|
|
193
199
|
echo -e "${CYAN}Diagnostics:${RESET}"
|
|
194
200
|
echo -e " ${YELLOW}ccs doctor${RESET} Run health check and diagnostics"
|
|
201
|
+
echo -e " ${YELLOW}ccs sync${RESET} Sync delegation commands and skills"
|
|
195
202
|
echo ""
|
|
203
|
+
|
|
196
204
|
echo -e "${CYAN}Flags:${RESET}"
|
|
197
205
|
echo -e " ${YELLOW}-h, --help${RESET} Show this help message"
|
|
198
206
|
echo -e " ${YELLOW}-v, --version${RESET} Show version and installation info"
|
|
199
|
-
echo -e " ${YELLOW}--shell-completion${RESET}
|
|
207
|
+
echo -e " ${YELLOW}-sc, --shell-completion${RESET} Install shell auto-completion"
|
|
200
208
|
echo ""
|
|
209
|
+
|
|
201
210
|
echo -e "${CYAN}Configuration:${RESET}"
|
|
202
|
-
echo -e " Config:
|
|
203
|
-
echo -e " Profiles:
|
|
204
|
-
echo -e " Instances:
|
|
205
|
-
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)"
|
|
206
216
|
echo ""
|
|
217
|
+
|
|
207
218
|
echo -e "${CYAN}Shared Data:${RESET}"
|
|
208
|
-
echo -e " Commands:
|
|
209
|
-
echo -e " Skills:
|
|
219
|
+
echo -e " Commands: ~/.ccs/shared/commands/"
|
|
220
|
+
echo -e " Skills: ~/.ccs/shared/skills/"
|
|
221
|
+
echo -e " Agents: ~/.ccs/shared/agents/"
|
|
210
222
|
echo -e " Note: Commands, skills, and agents are symlinked across all profiles"
|
|
211
223
|
echo ""
|
|
224
|
+
|
|
212
225
|
echo -e "${CYAN}Examples:${RESET}"
|
|
213
226
|
echo -e " ${YELLOW}\$ ccs${RESET} # Use default account"
|
|
214
227
|
echo -e " ${YELLOW}\$ ccs glm \"implement API\"${RESET} # Cost-optimized model"
|
|
215
228
|
echo ""
|
|
216
|
-
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}"
|
|
217
230
|
echo ""
|
|
231
|
+
|
|
218
232
|
echo -e "${YELLOW}Uninstall:${RESET}"
|
|
219
|
-
echo " npm: npm uninstall -g @kaitranntt/ccs"
|
|
220
|
-
echo " macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash"
|
|
221
|
-
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"
|
|
222
236
|
echo ""
|
|
237
|
+
|
|
223
238
|
echo -e "${CYAN}Documentation:${RESET}"
|
|
224
239
|
echo -e " GitHub: ${CYAN}https://github.com/kaitranntt/ccs${RESET}"
|
|
225
240
|
echo -e " Docs: https://github.com/kaitranntt/ccs/blob/main/README.md"
|
|
241
|
+
echo -e " Issues: https://github.com/kaitranntt/ccs/issues"
|
|
226
242
|
echo ""
|
|
243
|
+
|
|
227
244
|
echo -e "${CYAN}License:${RESET} MIT"
|
|
228
245
|
}
|
|
229
246
|
|
|
@@ -480,6 +497,100 @@ doctor_run() {
|
|
|
480
497
|
$has_errors && exit 1 || exit 0
|
|
481
498
|
}
|
|
482
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
|
+
|
|
483
594
|
# --- Claude CLI Detection Logic ---
|
|
484
595
|
|
|
485
596
|
detect_claude_cli() {
|
|
@@ -509,49 +620,81 @@ Restart your terminal after installation."
|
|
|
509
620
|
}
|
|
510
621
|
|
|
511
622
|
show_version() {
|
|
623
|
+
# Title
|
|
512
624
|
echo -e "${BOLD}CCS (Claude Code Switch) v${CCS_VERSION}${RESET}"
|
|
513
625
|
echo ""
|
|
626
|
+
|
|
627
|
+
# Installation section with table-like formatting
|
|
514
628
|
echo -e "${CYAN}Installation:${RESET}"
|
|
515
629
|
|
|
516
|
-
#
|
|
517
|
-
local
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
# Simple config display
|
|
521
|
-
local config="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
522
|
-
echo -e " ${CYAN}Config:${RESET} ${config}"
|
|
523
|
-
|
|
524
|
-
# Delegation status
|
|
525
|
-
local delegation_rules="$HOME/.ccs/delegation-rules.json"
|
|
526
|
-
if [[ -f "$delegation_rules" ]]; then
|
|
527
|
-
echo -e " ${CYAN}Delegation:${RESET} Enabled"
|
|
528
|
-
|
|
529
|
-
# Check which profiles are delegation-ready
|
|
530
|
-
local ready_profiles=()
|
|
531
|
-
for profile in glm kimi; do
|
|
532
|
-
local settings_file="$HOME/.ccs/profiles/$profile/settings.json"
|
|
533
|
-
if [[ -f "$settings_file" ]]; then
|
|
534
|
-
# Check if API key is configured (not a placeholder)
|
|
535
|
-
local api_key=$(jq -r '.env.ANTHROPIC_AUTH_TOKEN // empty' "$settings_file" 2>/dev/null)
|
|
536
|
-
if [[ -n "$api_key" ]] && [[ ! "$api_key" =~ YOUR_.*_API_KEY_HERE ]]; then
|
|
537
|
-
ready_profiles+=("$profile")
|
|
538
|
-
fi
|
|
539
|
-
fi
|
|
540
|
-
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)")
|
|
541
633
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
|
546
670
|
fi
|
|
671
|
+
done
|
|
672
|
+
|
|
673
|
+
if $delegation_configured; then
|
|
674
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Delegation:" "Enabled"
|
|
547
675
|
else
|
|
548
|
-
|
|
676
|
+
printf " ${CYAN}%-16s${RESET} %s\n" "Delegation:" "Not configured"
|
|
549
677
|
fi
|
|
678
|
+
|
|
550
679
|
echo ""
|
|
551
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
|
|
552
693
|
echo -e "${CYAN}Documentation:${RESET} https://github.com/kaitranntt/ccs"
|
|
553
694
|
echo -e "${CYAN}License:${RESET} MIT"
|
|
554
695
|
echo ""
|
|
696
|
+
|
|
697
|
+
# Help hint
|
|
555
698
|
echo -e "${YELLOW}Run 'ccs --help' for usage information${RESET}"
|
|
556
699
|
}
|
|
557
700
|
|
|
@@ -957,7 +1100,7 @@ detect_profile_type() {
|
|
|
957
1100
|
# --- Auth Commands (Phase 3) ---
|
|
958
1101
|
|
|
959
1102
|
auth_help() {
|
|
960
|
-
echo -e "${BOLD}CCS Account Management${RESET}"
|
|
1103
|
+
echo -e "${BOLD}CCS Concurrent Account Management${RESET}"
|
|
961
1104
|
echo ""
|
|
962
1105
|
echo -e "${CYAN}Usage:${RESET}"
|
|
963
1106
|
echo -e " ${YELLOW}ccs auth${RESET} <command> [options]"
|
|
@@ -1509,7 +1652,7 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "auth" ]]; then
|
|
|
1509
1652
|
fi
|
|
1510
1653
|
|
|
1511
1654
|
# Special case: shell completion installer
|
|
1512
|
-
if [[ $# -gt 0 ]] && [[ "${1}" == "--shell-completion" ]]; then
|
|
1655
|
+
if [[ $# -gt 0 ]] && [[ "${1}" == "--shell-completion" || "${1}" == "-sc" ]]; then
|
|
1513
1656
|
install_shell_completion "$@"
|
|
1514
1657
|
exit $?
|
|
1515
1658
|
fi
|
|
@@ -1520,6 +1663,12 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "doctor" || "${1}" == "--doctor" ]]; then
|
|
|
1520
1663
|
exit $?
|
|
1521
1664
|
fi
|
|
1522
1665
|
|
|
1666
|
+
# Special case: sync command
|
|
1667
|
+
if [[ $# -gt 0 ]] && [[ "${1}" == "sync" || "${1}" == "--sync" ]]; then
|
|
1668
|
+
sync_run
|
|
1669
|
+
exit $?
|
|
1670
|
+
fi
|
|
1671
|
+
|
|
1523
1672
|
# Run auto-recovery before main logic
|
|
1524
1673
|
auto_recover || {
|
|
1525
1674
|
msg_error "Auto-recovery failed. Check permissions."
|
package/lib/ccs.ps1
CHANGED
|
@@ -12,7 +12,7 @@ param(
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
13
|
|
|
14
14
|
# Version (updated by scripts/bump-version.sh)
|
|
15
|
-
$CcsVersion = "4.1.
|
|
15
|
+
$CcsVersion = "4.1.5"
|
|
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"
|
|
@@ -212,66 +212,105 @@ function Show-Help {
|
|
|
212
212
|
|
|
213
213
|
Write-ColorLine "CCS (Claude Code Switch) - Instant profile switching for Claude CLI" "White"
|
|
214
214
|
Write-Host ""
|
|
215
|
+
|
|
215
216
|
Write-ColorLine "Usage:" "Cyan"
|
|
216
217
|
Write-ColorLine " ccs [profile] [claude-args...]" "Yellow"
|
|
217
|
-
Write-ColorLine " ccs auth <command> [options]" "Yellow"
|
|
218
218
|
Write-ColorLine " ccs [flags]" "Yellow"
|
|
219
219
|
Write-Host ""
|
|
220
|
+
|
|
220
221
|
Write-ColorLine "Description:" "Cyan"
|
|
221
222
|
Write-Host " Switch between multiple Claude accounts and alternative models"
|
|
222
223
|
Write-Host " (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently"
|
|
223
224
|
Write-Host " with auto-recovery. Zero downtime."
|
|
224
225
|
Write-Host ""
|
|
226
|
+
|
|
225
227
|
Write-ColorLine "Model Switching:" "Cyan"
|
|
226
228
|
Write-ColorLine " ccs Use default Claude account" "Yellow"
|
|
227
229
|
Write-ColorLine " ccs glm Switch to GLM 4.6 model" "Yellow"
|
|
228
230
|
Write-ColorLine " ccs glmt Switch to GLM with thinking mode" "Yellow"
|
|
231
|
+
Write-ColorLine " ccs glmt --verbose Enable debug logging" "Yellow"
|
|
229
232
|
Write-ColorLine " ccs kimi Switch to Kimi for Coding" "Yellow"
|
|
230
233
|
Write-ColorLine " ccs glm 'debug this code' Use GLM and run command" "Yellow"
|
|
231
234
|
Write-Host ""
|
|
232
|
-
|
|
233
|
-
Write-ColorLine " `$ ccs" "Yellow" -NoNewline
|
|
234
|
-
Write-Host " # Use default account"
|
|
235
|
-
Write-ColorLine " `$ ccs glm `"implement API`"" "Yellow" -NoNewline
|
|
236
|
-
Write-Host " # Cost-optimized model"
|
|
237
|
-
Write-Host ""
|
|
235
|
+
|
|
238
236
|
Write-ColorLine "Account Management:" "Cyan"
|
|
239
|
-
Write-ColorLine " ccs auth --help
|
|
237
|
+
Write-ColorLine " ccs auth --help Run multiple Claude accounts concurrently" "Yellow"
|
|
240
238
|
Write-Host ""
|
|
239
|
+
|
|
241
240
|
Write-ColorLine "Delegation (inside Claude Code CLI):" "Cyan"
|
|
242
241
|
Write-ColorLine " /ccs:glm `"task`" Delegate to GLM-4.6 for simple tasks" "Yellow"
|
|
243
242
|
Write-ColorLine " /ccs:kimi `"task`" Delegate to Kimi for long context" "Yellow"
|
|
244
243
|
Write-Host " Save tokens by delegating simple tasks to cost-optimized models"
|
|
245
244
|
Write-Host ""
|
|
245
|
+
|
|
246
246
|
Write-ColorLine "Diagnostics:" "Cyan"
|
|
247
247
|
Write-ColorLine " ccs doctor Run health check and diagnostics" "Yellow"
|
|
248
|
+
Write-ColorLine " ccs sync Sync delegation commands and skills" "Yellow"
|
|
248
249
|
Write-Host ""
|
|
250
|
+
|
|
249
251
|
Write-ColorLine "Flags:" "Cyan"
|
|
250
252
|
Write-ColorLine " -h, --help Show this help message" "Yellow"
|
|
251
253
|
Write-ColorLine " -v, --version Show version and installation info" "Yellow"
|
|
252
|
-
Write-ColorLine " --shell-completion
|
|
254
|
+
Write-ColorLine " -sc, --shell-completion Install shell auto-completion" "Yellow"
|
|
253
255
|
Write-Host ""
|
|
256
|
+
|
|
254
257
|
Write-ColorLine "Configuration:" "Cyan"
|
|
255
|
-
Write-Host " Config:
|
|
256
|
-
Write-Host " Profiles:
|
|
257
|
-
Write-Host " Instances:
|
|
258
|
-
Write-Host " Settings:
|
|
258
|
+
Write-Host " Config File: ~/.ccs/config.json"
|
|
259
|
+
Write-Host " Profiles: ~/.ccs/profiles.json"
|
|
260
|
+
Write-Host " Instances: ~/.ccs/instances/"
|
|
261
|
+
Write-Host " Settings: ~/.ccs/*.settings.json"
|
|
262
|
+
Write-Host " Environment: CCS_CONFIG (override config path)"
|
|
259
263
|
Write-Host ""
|
|
264
|
+
|
|
260
265
|
Write-ColorLine "Shared Data:" "Cyan"
|
|
261
|
-
Write-Host " Commands:
|
|
262
|
-
Write-Host " Skills:
|
|
266
|
+
Write-Host " Commands: ~/.ccs/shared/commands/"
|
|
267
|
+
Write-Host " Skills: ~/.ccs/shared/skills/"
|
|
268
|
+
Write-Host " Agents: ~/.ccs/shared/agents/"
|
|
263
269
|
Write-Host " Note: Commands, skills, and agents are symlinked across all profiles"
|
|
264
270
|
Write-Host ""
|
|
271
|
+
|
|
272
|
+
Write-ColorLine "Examples:" "Cyan"
|
|
273
|
+
Write-ColorLine " `$ ccs # Use default account" "Yellow"
|
|
274
|
+
Write-ColorLine " `$ ccs glm `"implement API`" # Cost-optimized model" "Yellow"
|
|
275
|
+
Write-Host ""
|
|
276
|
+
Write-ColorLine " For more: https://github.com/kaitranntt/ccs/blob/main/README.md" "Cyan"
|
|
277
|
+
Write-Host ""
|
|
278
|
+
|
|
279
|
+
Write-ColorLine "Uninstall:" "Yellow"
|
|
280
|
+
Write-Host " npm: npm uninstall -g @kaitranntt/ccs"
|
|
281
|
+
Write-Host " macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash"
|
|
282
|
+
Write-Host " Windows: irm ccs.kaitran.ca/uninstall | iex"
|
|
283
|
+
Write-Host ""
|
|
284
|
+
|
|
265
285
|
Write-ColorLine "Documentation:" "Cyan"
|
|
266
286
|
Write-Host " GitHub: https://github.com/kaitranntt/ccs"
|
|
267
287
|
Write-Host " Docs: https://github.com/kaitranntt/ccs/blob/main/README.md"
|
|
288
|
+
Write-Host " Issues: https://github.com/kaitranntt/ccs/issues"
|
|
268
289
|
Write-Host ""
|
|
290
|
+
|
|
269
291
|
Write-ColorLine "License: MIT" "Cyan"
|
|
270
292
|
}
|
|
271
293
|
|
|
272
294
|
function Show-Version {
|
|
273
295
|
$UseColors = $env:FORCE_COLOR -or ([Console]::IsOutputRedirected -eq $false -and -not $env:NO_COLOR)
|
|
274
296
|
|
|
297
|
+
# Helper for aligned output
|
|
298
|
+
function Write-TableLine {
|
|
299
|
+
param(
|
|
300
|
+
[string]$Label,
|
|
301
|
+
[string]$Value,
|
|
302
|
+
[string]$Color = "Cyan"
|
|
303
|
+
)
|
|
304
|
+
if ($UseColors) {
|
|
305
|
+
$PaddedLabel = $Label.PadRight(17)
|
|
306
|
+
Write-Host " $PaddedLabel " -ForegroundColor $Color -NoNewline
|
|
307
|
+
Write-Host $Value
|
|
308
|
+
} else {
|
|
309
|
+
$PaddedLabel = $Label.PadRight(17)
|
|
310
|
+
Write-Host " $PaddedLabel $Value"
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
275
314
|
# Title
|
|
276
315
|
if ($UseColors) {
|
|
277
316
|
Write-Host "CCS (Claude Code Switch) v$CcsVersion" -ForegroundColor White
|
|
@@ -280,38 +319,91 @@ function Show-Version {
|
|
|
280
319
|
}
|
|
281
320
|
Write-Host ""
|
|
282
321
|
|
|
283
|
-
# Installation
|
|
322
|
+
# Installation section with table-like formatting
|
|
284
323
|
if ($UseColors) { Write-Host "Installation:" -ForegroundColor Cyan }
|
|
285
324
|
else { Write-Host "Installation:" }
|
|
286
325
|
|
|
287
|
-
# Location
|
|
326
|
+
# Location - prioritize script location over command location
|
|
327
|
+
$ScriptLocation = $MyInvocation.MyCommand.Path
|
|
288
328
|
$InstallLocation = (Get-Command ccs -ErrorAction SilentlyContinue).Source
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
329
|
+
|
|
330
|
+
# Show script location if running from source
|
|
331
|
+
if ($ScriptLocation -and (Test-Path $ScriptLocation)) {
|
|
332
|
+
Write-TableLine "Location:" $ScriptLocation
|
|
333
|
+
} elseif ($InstallLocation) {
|
|
334
|
+
Write-TableLine "Location:" $InstallLocation
|
|
296
335
|
} else {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
336
|
+
Write-TableLine "Location:" "(not found - run from current directory)"
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
# .ccs/ directory location
|
|
340
|
+
Write-TableLine "CCS Directory:" "$env:USERPROFILE\.ccs\"
|
|
341
|
+
|
|
342
|
+
# Config path
|
|
343
|
+
Write-TableLine "Config:" $ConfigFile
|
|
344
|
+
|
|
345
|
+
# Profiles.json location
|
|
346
|
+
Write-TableLine "Profiles:" $ProfilesJson
|
|
347
|
+
|
|
348
|
+
# Delegation status - check multiple indicators
|
|
349
|
+
$DelegationConfigured = $false
|
|
350
|
+
$ReadyProfiles = @()
|
|
351
|
+
|
|
352
|
+
# Check for delegation-sessions.json (primary indicator)
|
|
353
|
+
$DelegationSessions = "$env:USERPROFILE\.ccs\delegation-sessions.json"
|
|
354
|
+
if (Test-Path $DelegationSessions) {
|
|
355
|
+
$DelegationConfigured = $true
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
# Check for profiles with valid API keys (secondary indicator)
|
|
359
|
+
foreach ($profile in @("glm", "kimi")) {
|
|
360
|
+
$SettingsFile = "$env:USERPROFILE\.ccs\$profile.settings.json"
|
|
361
|
+
if (Test-Path $SettingsFile) {
|
|
362
|
+
try {
|
|
363
|
+
$Settings = Get-Content $SettingsFile -Raw | ConvertFrom-Json
|
|
364
|
+
$ApiKey = $Settings.env.ANTHROPIC_AUTH_TOKEN
|
|
365
|
+
if ($ApiKey -and $ApiKey -notmatch "YOUR_.*_API_KEY_HERE" -and $ApiKey -notmatch "sk-test.*") {
|
|
366
|
+
$ReadyProfiles += $profile
|
|
367
|
+
$DelegationConfigured = $true
|
|
368
|
+
}
|
|
369
|
+
} catch { }
|
|
302
370
|
}
|
|
303
371
|
}
|
|
304
372
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
Write-Host " Config: " -ForegroundColor Cyan -NoNewline
|
|
308
|
-
Write-Host $ConfigFile
|
|
373
|
+
if ($DelegationConfigured) {
|
|
374
|
+
Write-TableLine "Delegation:" "Enabled"
|
|
309
375
|
} else {
|
|
310
|
-
Write-
|
|
376
|
+
Write-TableLine "Delegation:" "Not configured"
|
|
311
377
|
}
|
|
312
378
|
|
|
313
379
|
Write-Host ""
|
|
314
380
|
|
|
381
|
+
# Ready Profiles section - make it more prominent
|
|
382
|
+
if ($ReadyProfiles.Count -gt 0) {
|
|
383
|
+
if ($UseColors) { Write-Host "Delegation Ready:" -ForegroundColor Cyan }
|
|
384
|
+
else { Write-Host "Delegation Ready:" }
|
|
385
|
+
|
|
386
|
+
$ReadyProfilesStr = $ReadyProfiles -join ", "
|
|
387
|
+
if ($UseColors) {
|
|
388
|
+
Write-Host " ✓ " -ForegroundColor Yellow -NoNewline
|
|
389
|
+
Write-Host "$ReadyProfilesStr profiles are ready for delegation"
|
|
390
|
+
} else {
|
|
391
|
+
Write-Host " ! $ReadyProfilesStr profiles are ready for delegation"
|
|
392
|
+
}
|
|
393
|
+
Write-Host ""
|
|
394
|
+
} elseif ($DelegationConfigured) {
|
|
395
|
+
if ($UseColors) { Write-Host "Delegation Ready:" -ForegroundColor Cyan }
|
|
396
|
+
else { Write-Host "Delegation Ready:" }
|
|
397
|
+
|
|
398
|
+
if ($UseColors) {
|
|
399
|
+
Write-Host " ! " -ForegroundColor Yellow -NoNewline
|
|
400
|
+
Write-Host "Delegation configured but no valid API keys found"
|
|
401
|
+
} else {
|
|
402
|
+
Write-Host " ! Delegation configured but no valid API keys found"
|
|
403
|
+
}
|
|
404
|
+
Write-Host ""
|
|
405
|
+
}
|
|
406
|
+
|
|
315
407
|
# Documentation
|
|
316
408
|
if ($UseColors) {
|
|
317
409
|
Write-Host "Documentation: https://github.com/kaitranntt/ccs" -ForegroundColor Cyan
|
|
@@ -845,11 +937,105 @@ function Get-ProfileType {
|
|
|
845
937
|
}
|
|
846
938
|
}
|
|
847
939
|
|
|
940
|
+
# --- Sync Command ---
|
|
941
|
+
|
|
942
|
+
function Sync-Run {
|
|
943
|
+
$CcsClaudeDir = "$env:USERPROFILE\.ccs\.claude"
|
|
944
|
+
$UserClaudeDir = "$env:USERPROFILE\.claude"
|
|
945
|
+
|
|
946
|
+
Write-Host "Syncing delegation commands and skills to ~/.claude/..." -ForegroundColor Cyan
|
|
947
|
+
Write-Host ""
|
|
948
|
+
|
|
949
|
+
# Check if source directory exists
|
|
950
|
+
if (-not (Test-Path $CcsClaudeDir)) {
|
|
951
|
+
Write-Host "[X] CCS .claude/ directory not found at $CcsClaudeDir" -ForegroundColor Red
|
|
952
|
+
Write-Host "Reinstall CCS: npm install -g @kaitranntt/ccs --force"
|
|
953
|
+
exit 1
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
# Create ~/.claude/ if missing
|
|
957
|
+
if (-not (Test-Path $UserClaudeDir)) {
|
|
958
|
+
Write-Host "[i] Creating ~/.claude/ directory" -ForegroundColor Cyan
|
|
959
|
+
New-Item -ItemType Directory -Path $UserClaudeDir -Force | Out-Null
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
# Items to symlink
|
|
963
|
+
$Items = @(
|
|
964
|
+
@{ Source = "commands\ccs"; Target = "commands\ccs"; Type = "Directory" }
|
|
965
|
+
@{ Source = "skills\ccs-delegation"; Target = "skills\ccs-delegation"; Type = "Directory" }
|
|
966
|
+
@{ Source = "agents\ccs-delegator.md"; Target = "agents\ccs-delegator.md"; Type = "File" }
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
$Installed = 0
|
|
970
|
+
$Skipped = 0
|
|
971
|
+
|
|
972
|
+
foreach ($Item in $Items) {
|
|
973
|
+
$SourcePath = Join-Path $CcsClaudeDir $Item.Source
|
|
974
|
+
$TargetPath = Join-Path $UserClaudeDir $Item.Target
|
|
975
|
+
$TargetDir = Split-Path -Parent $TargetPath
|
|
976
|
+
|
|
977
|
+
# Check source exists
|
|
978
|
+
if (-not (Test-Path $SourcePath)) {
|
|
979
|
+
Write-Host "[!] Source not found: $($Item.Source), skipping" -ForegroundColor Yellow
|
|
980
|
+
continue
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
# Create parent directory if needed
|
|
984
|
+
if (-not (Test-Path $TargetDir)) {
|
|
985
|
+
New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
# Check if already correct symlink
|
|
989
|
+
if (Test-Path $TargetPath) {
|
|
990
|
+
$ItemInfo = Get-Item $TargetPath -Force
|
|
991
|
+
if ($ItemInfo.LinkType -eq "SymbolicLink") {
|
|
992
|
+
$LinkTarget = $ItemInfo.Target
|
|
993
|
+
if ($LinkTarget -eq $SourcePath) {
|
|
994
|
+
$Skipped++
|
|
995
|
+
continue
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
# Backup existing file/directory
|
|
1000
|
+
$Timestamp = Get-Date -Format "yyyy-MM-dd"
|
|
1001
|
+
$BackupPath = "$TargetPath.backup-$Timestamp"
|
|
1002
|
+
$Counter = 1
|
|
1003
|
+
|
|
1004
|
+
while (Test-Path $BackupPath) {
|
|
1005
|
+
$BackupPath = "$TargetPath.backup-$Timestamp-$Counter"
|
|
1006
|
+
$Counter++
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
Move-Item -Path $TargetPath -Destination $BackupPath -Force
|
|
1010
|
+
Write-Host "[i] Backed up existing to $(Split-Path -Leaf $BackupPath)" -ForegroundColor Cyan
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
# Create symlink
|
|
1014
|
+
try {
|
|
1015
|
+
$SymlinkType = if ($Item.Type -eq "Directory") { "Junction" } else { "SymbolicLink" }
|
|
1016
|
+
New-Item -ItemType $SymlinkType -Path $TargetPath -Target $SourcePath -Force -ErrorAction Stop | Out-Null
|
|
1017
|
+
Write-Host "[OK] Installed $($Item.Target)" -ForegroundColor Green
|
|
1018
|
+
$Installed++
|
|
1019
|
+
} catch {
|
|
1020
|
+
Write-Host "[X] Failed to install $($Item.Target): $($_.Exception.Message)" -ForegroundColor Red
|
|
1021
|
+
if ($_.Exception.Message -match "privilege") {
|
|
1022
|
+
Write-Host "[i] Run PowerShell as Administrator or enable Developer Mode" -ForegroundColor Yellow
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
Write-Host ""
|
|
1028
|
+
Write-Host "✓ Update complete" -ForegroundColor Green
|
|
1029
|
+
Write-Host " Installed: $Installed"
|
|
1030
|
+
Write-Host " Already up-to-date: $Skipped"
|
|
1031
|
+
Write-Host ""
|
|
1032
|
+
}
|
|
1033
|
+
|
|
848
1034
|
# --- Auth Commands (Phase 3) ---
|
|
849
1035
|
|
|
850
1036
|
function Show-AuthHelp {
|
|
851
1037
|
Write-Host ""
|
|
852
|
-
Write-Host "CCS Account Management" -ForegroundColor White
|
|
1038
|
+
Write-Host "CCS Concurrent Account Management" -ForegroundColor White
|
|
853
1039
|
Write-Host ""
|
|
854
1040
|
Write-Host "Usage:" -ForegroundColor Cyan
|
|
855
1041
|
Write-Host " ccs auth <command> [options]" -ForegroundColor Yellow
|
|
@@ -1314,7 +1500,7 @@ if ($Help) {
|
|
|
1314
1500
|
}
|
|
1315
1501
|
|
|
1316
1502
|
# Special case: shell completion installer
|
|
1317
|
-
if ($RemainingArgs.Count -gt 0 -and $RemainingArgs[0] -eq "--shell-completion") {
|
|
1503
|
+
if ($RemainingArgs.Count -gt 0 -and ($RemainingArgs[0] -eq "--shell-completion" -or $RemainingArgs[0] -eq "-sc")) {
|
|
1318
1504
|
$CompletionArgs = if ($RemainingArgs.Count -gt 1) { $RemainingArgs[1..($RemainingArgs.Count-1)] } else { @() }
|
|
1319
1505
|
$Result = Install-ShellCompletion $CompletionArgs
|
|
1320
1506
|
exit $Result
|
|
@@ -1327,6 +1513,12 @@ if ($RemainingArgs.Count -gt 0 -and $RemainingArgs[0] -eq "auth") {
|
|
|
1327
1513
|
exit $LASTEXITCODE
|
|
1328
1514
|
}
|
|
1329
1515
|
|
|
1516
|
+
# Special case: sync command
|
|
1517
|
+
if ($RemainingArgs.Count -gt 0 -and ($RemainingArgs[0] -eq "sync" -or $RemainingArgs[0] -eq "--sync")) {
|
|
1518
|
+
Sync-Run
|
|
1519
|
+
exit 0
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1330
1522
|
# Run auto-recovery before main logic
|
|
1331
1523
|
if (-not (Invoke-AutoRecovery)) {
|
|
1332
1524
|
Write-ErrorMsg "Auto-recovery failed. Check permissions."
|
package/package.json
CHANGED
|
@@ -18,8 +18,8 @@ _ccs_completion() {
|
|
|
18
18
|
|
|
19
19
|
# Top-level completion (first argument)
|
|
20
20
|
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
|
21
|
-
local commands="auth doctor"
|
|
22
|
-
local flags="--help --version --shell-completion -h -v"
|
|
21
|
+
local commands="auth doctor sync"
|
|
22
|
+
local flags="--help --version --shell-completion -h -v -sc"
|
|
23
23
|
local profiles=""
|
|
24
24
|
|
|
25
25
|
# Add profiles from config.json (settings-based profiles)
|
|
@@ -70,29 +70,30 @@ complete -c ccs -f
|
|
|
70
70
|
# Top-level flags
|
|
71
71
|
complete -c ccs -s h -l help -d 'Show help message'
|
|
72
72
|
complete -c ccs -s v -l version -d 'Show version information'
|
|
73
|
-
complete -c ccs -l shell-completion -d 'Install shell completion'
|
|
73
|
+
complete -c ccs -s sc -l shell-completion -d 'Install shell completion'
|
|
74
74
|
|
|
75
75
|
# Top-level commands (blue color for commands)
|
|
76
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a 'auth' -d (set_color blue)'Manage multiple Claude accounts'(set_color normal)
|
|
77
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a 'doctor' -d (set_color blue)'Run health check and diagnostics'(set_color normal)
|
|
76
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'auth' -d (set_color blue)'Manage multiple Claude accounts'(set_color normal)
|
|
77
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'doctor' -d (set_color blue)'Run health check and diagnostics'(set_color normal)
|
|
78
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'sync' -d (set_color blue)'Sync delegation commands and skills'(set_color normal)
|
|
78
79
|
|
|
79
80
|
# Top-level known settings profiles (green color for model profiles)
|
|
80
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a 'default' -d (set_color green)'Default Claude Sonnet 4.5'(set_color normal)
|
|
81
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a 'glm' -d (set_color green)'GLM-4.6 (cost-optimized)'(set_color normal)
|
|
82
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a 'glmt' -d (set_color green)'GLM-4.6 with thinking mode'(set_color normal)
|
|
83
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a 'kimi' -d (set_color green)'Kimi for Coding (long-context)'(set_color normal)
|
|
81
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'default' -d (set_color green)'Default Claude Sonnet 4.5'(set_color normal)
|
|
82
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'glm' -d (set_color green)'GLM-4.6 (cost-optimized)'(set_color normal)
|
|
83
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'glmt' -d (set_color green)'GLM-4.6 with thinking mode'(set_color normal)
|
|
84
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a 'kimi' -d (set_color green)'Kimi for Coding (long-context)'(set_color normal)
|
|
84
85
|
|
|
85
86
|
# Top-level custom settings profiles (dynamic, with generic description in green)
|
|
86
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a '(__fish_ccs_get_custom_settings_profiles)' -d (set_color green)'Settings-based profile'(set_color normal)
|
|
87
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a '(__fish_ccs_get_custom_settings_profiles)' -d (set_color green)'Settings-based profile'(set_color normal)
|
|
87
88
|
|
|
88
89
|
# Top-level account profiles (dynamic, yellow color for account profiles)
|
|
89
|
-
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor' -a '(__fish_ccs_get_account_profiles)' -d (set_color yellow)'Account profile'(set_color normal)
|
|
90
|
+
complete -c ccs -n 'not __fish_seen_subcommand_from auth doctor sync' -a '(__fish_ccs_get_account_profiles)' -d (set_color yellow)'Account profile'(set_color normal)
|
|
90
91
|
|
|
91
92
|
# shell-completion subflags
|
|
92
|
-
complete -c ccs -n '__fish_seen_argument -l shell-completion' -l bash -d 'Install for bash'
|
|
93
|
-
complete -c ccs -n '__fish_seen_argument -l shell-completion' -l zsh -d 'Install for zsh'
|
|
94
|
-
complete -c ccs -n '__fish_seen_argument -l shell-completion' -l fish -d 'Install for fish'
|
|
95
|
-
complete -c ccs -n '__fish_seen_argument -l shell-completion' -l powershell -d 'Install for PowerShell'
|
|
93
|
+
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l bash -d 'Install for bash'
|
|
94
|
+
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l zsh -d 'Install for zsh'
|
|
95
|
+
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l fish -d 'Install for fish'
|
|
96
|
+
complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l powershell -d 'Install for PowerShell'
|
|
96
97
|
|
|
97
98
|
# auth subcommands
|
|
98
99
|
complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'create' -d 'Create new profile and login'
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
Register-ArgumentCompleter -CommandName ccs -ScriptBlock {
|
|
13
13
|
param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
14
14
|
|
|
15
|
-
$commands = @('auth', 'doctor', '--help', '--version', '--shell-completion', '-h', '-v')
|
|
15
|
+
$commands = @('auth', 'doctor', 'sync', '--help', '--version', '--shell-completion', '-h', '-v', '-sc')
|
|
16
16
|
$authCommands = @('create', 'list', 'show', 'remove', 'default', '--help', '-h')
|
|
17
17
|
$shellCompletionFlags = @('--bash', '--zsh', '--fish', '--powershell')
|
|
18
18
|
$listFlags = @('--verbose', '--json')
|
|
@@ -69,7 +69,7 @@ Register-ArgumentCompleter -CommandName ccs -ScriptBlock {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
# shell-completion flag completion
|
|
72
|
-
if ($words[1] -eq '--shell-completion') {
|
|
72
|
+
if ($words[1] -eq '--shell-completion' -or $words[1] -eq '-sc') {
|
|
73
73
|
if ($position -eq 3) {
|
|
74
74
|
$shellCompletionFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
75
75
|
[System.Management.Automation.CompletionResult]::new(
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# Color codes: 0;34=blue, 0;32=green, 0;33=yellow, 2;37=dim white
|
|
17
17
|
# Pattern format: =(#b)(group1)(group2)==color_for_group1=color_for_group2
|
|
18
18
|
# The leading '=' means no color for whole match, then each '=' assigns to each group
|
|
19
|
-
zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|doctor)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37'
|
|
19
|
+
zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|doctor|sync)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37'
|
|
20
20
|
zstyle ':completion:*:*:ccs:*:model-profiles' list-colors '=(#b)(default|glm|glmt|kimi|[^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;32=2\;37'
|
|
21
21
|
zstyle ':completion:*:*:ccs:*:account-profiles' list-colors '=(#b)([^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;33=2\;37'
|
|
22
22
|
zstyle ':completion:*:*:ccs:*' group-name ''
|
|
@@ -34,6 +34,7 @@ _ccs() {
|
|
|
34
34
|
commands=(
|
|
35
35
|
'auth:Manage multiple Claude accounts'
|
|
36
36
|
'doctor:Run health check and diagnostics'
|
|
37
|
+
'sync:Sync delegation commands and skills'
|
|
37
38
|
)
|
|
38
39
|
|
|
39
40
|
# Define known settings profiles with descriptions (consistent padding)
|
|
@@ -71,7 +72,7 @@ _ccs() {
|
|
|
71
72
|
_arguments -C \
|
|
72
73
|
'(- *)'{-h,--help}'[Show help message]' \
|
|
73
74
|
'(- *)'{-v,--version}'[Show version information]' \
|
|
74
|
-
'(- *)
|
|
75
|
+
'(- *)'{-sc,--shell-completion}'[Install shell completion]' \
|
|
75
76
|
'1: :->command' \
|
|
76
77
|
'*:: :->args'
|
|
77
78
|
|