@kaitranntt/ccs 3.5.0 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/ccs-delegator.md +117 -0
- package/.claude/commands/ccs/glm/continue.md +22 -0
- package/.claude/commands/ccs/glm.md +22 -0
- package/.claude/commands/ccs/kimi/continue.md +22 -0
- package/.claude/commands/ccs/kimi.md +22 -0
- package/.claude/skills/ccs-delegation/SKILL.md +54 -0
- package/.claude/skills/ccs-delegation/references/README.md +24 -0
- package/.claude/skills/ccs-delegation/references/delegation-guidelines.md +99 -0
- package/.claude/skills/ccs-delegation/references/headless-workflow.md +174 -0
- package/.claude/skills/ccs-delegation/references/troubleshooting.md +268 -0
- package/README.md +223 -23
- package/VERSION +1 -1
- package/bin/ccs.js +67 -0
- package/bin/delegation/README.md +189 -0
- package/bin/delegation/delegation-handler.js +212 -0
- package/bin/delegation/headless-executor.js +618 -0
- package/bin/delegation/result-formatter.js +485 -0
- package/bin/delegation/session-manager.js +157 -0
- package/bin/delegation/settings-parser.js +109 -0
- package/bin/management/doctor.js +94 -1
- package/bin/utils/claude-dir-installer.js +102 -0
- package/bin/utils/claude-symlink-manager.js +238 -0
- package/bin/utils/delegation-validator.js +154 -0
- package/lib/ccs +35 -1
- package/lib/ccs.ps1 +1 -1
- package/package.json +2 -1
- package/scripts/postinstall.js +22 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validates delegation profiles for CCS delegation system
|
|
10
|
+
* Ensures profiles exist and have valid API keys configured
|
|
11
|
+
*/
|
|
12
|
+
class DelegationValidator {
|
|
13
|
+
/**
|
|
14
|
+
* Validate a delegation profile
|
|
15
|
+
* @param {string} profileName - Name of profile to validate (e.g., 'glm', 'kimi')
|
|
16
|
+
* @returns {Object} Validation result { valid: boolean, error?: string, settingsPath?: string }
|
|
17
|
+
*/
|
|
18
|
+
static validate(profileName) {
|
|
19
|
+
const homeDir = os.homedir();
|
|
20
|
+
const settingsPath = path.join(homeDir, '.ccs', `${profileName}.settings.json`);
|
|
21
|
+
|
|
22
|
+
// Check if profile directory exists
|
|
23
|
+
if (!fs.existsSync(settingsPath)) {
|
|
24
|
+
return {
|
|
25
|
+
valid: false,
|
|
26
|
+
error: `Profile not found: ${profileName}`,
|
|
27
|
+
suggestion: `Profile settings missing at: ${settingsPath}\n\n` +
|
|
28
|
+
`To set up ${profileName} profile:\n` +
|
|
29
|
+
` 1. Copy base settings: cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json\n` +
|
|
30
|
+
` 2. Edit settings: Edit ~/.ccs/${profileName}.settings.json\n` +
|
|
31
|
+
` 3. Set your API key in ANTHROPIC_AUTH_TOKEN field`
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Read and parse settings.json
|
|
36
|
+
let settings;
|
|
37
|
+
try {
|
|
38
|
+
const settingsContent = fs.readFileSync(settingsPath, 'utf8');
|
|
39
|
+
settings = JSON.parse(settingsContent);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
valid: false,
|
|
43
|
+
error: `Failed to parse settings.json for ${profileName}`,
|
|
44
|
+
suggestion: `Settings file is corrupted or invalid JSON.\n\n` +
|
|
45
|
+
`Location: ${settingsPath}\n` +
|
|
46
|
+
`Parse error: ${error.message}\n\n` +
|
|
47
|
+
`Fix: Restore from base config:\n` +
|
|
48
|
+
` cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Validate API key exists and is not default
|
|
53
|
+
const apiKey = settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
54
|
+
|
|
55
|
+
if (!apiKey) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
error: `API key not configured for ${profileName}`,
|
|
59
|
+
suggestion: `Missing ANTHROPIC_AUTH_TOKEN in settings.\n\n` +
|
|
60
|
+
`Edit: ${settingsPath}\n` +
|
|
61
|
+
`Set: env.ANTHROPIC_AUTH_TOKEN to your API key`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check for default placeholder values
|
|
66
|
+
const defaultPlaceholders = [
|
|
67
|
+
'YOUR_GLM_API_KEY_HERE',
|
|
68
|
+
'YOUR_KIMI_API_KEY_HERE',
|
|
69
|
+
'YOUR_API_KEY_HERE',
|
|
70
|
+
'your-api-key-here',
|
|
71
|
+
'PLACEHOLDER'
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
if (defaultPlaceholders.some(placeholder => apiKey.includes(placeholder))) {
|
|
75
|
+
return {
|
|
76
|
+
valid: false,
|
|
77
|
+
error: `Default API key placeholder detected for ${profileName}`,
|
|
78
|
+
suggestion: `API key is still set to default placeholder.\n\n` +
|
|
79
|
+
`To configure your profile:\n` +
|
|
80
|
+
` 1. Edit: ${settingsPath}\n` +
|
|
81
|
+
` 2. Replace ANTHROPIC_AUTH_TOKEN with your actual API key\n\n` +
|
|
82
|
+
`Get API key:\n` +
|
|
83
|
+
` GLM: https://z.ai/manage-apikey/apikey-list\n` +
|
|
84
|
+
` Kimi: https://platform.moonshot.cn/console/api-keys`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Validation passed
|
|
89
|
+
return {
|
|
90
|
+
valid: true,
|
|
91
|
+
settingsPath,
|
|
92
|
+
apiKey: apiKey.substring(0, 8) + '...' // Show first 8 chars for verification
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Format validation error for display
|
|
98
|
+
* @param {Object} result - Validation result from validate()
|
|
99
|
+
* @returns {string} Formatted error message
|
|
100
|
+
*/
|
|
101
|
+
static formatError(result) {
|
|
102
|
+
if (result.valid) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let message = `\n[X] ${result.error}\n\n`;
|
|
107
|
+
|
|
108
|
+
if (result.suggestion) {
|
|
109
|
+
message += `${result.suggestion}\n`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return message;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Check if profile is delegation-ready (shorthand)
|
|
117
|
+
* @param {string} profileName - Profile to check
|
|
118
|
+
* @returns {boolean} True if ready for delegation
|
|
119
|
+
*/
|
|
120
|
+
static isReady(profileName) {
|
|
121
|
+
const result = this.validate(profileName);
|
|
122
|
+
return result.valid;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get all delegation-ready profiles
|
|
127
|
+
* @returns {Array<string>} List of profile names ready for delegation
|
|
128
|
+
*/
|
|
129
|
+
static getReadyProfiles() {
|
|
130
|
+
const homeDir = os.homedir();
|
|
131
|
+
const ccsDir = path.join(homeDir, '.ccs');
|
|
132
|
+
|
|
133
|
+
if (!fs.existsSync(ccsDir)) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const profiles = [];
|
|
138
|
+
const entries = fs.readdirSync(ccsDir, { withFileTypes: true });
|
|
139
|
+
|
|
140
|
+
// Look for *.settings.json files
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
if (entry.isFile() && entry.name.endsWith('.settings.json')) {
|
|
143
|
+
const profileName = entry.name.replace('.settings.json', '');
|
|
144
|
+
if (this.isReady(profileName)) {
|
|
145
|
+
profiles.push(profileName);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return profiles;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { DelegationValidator };
|
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="
|
|
5
|
+
CCS_VERSION="4.1.1"
|
|
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"
|
|
@@ -187,6 +187,13 @@ show_help() {
|
|
|
187
187
|
echo -e " ${YELLOW}ccs work${RESET} Switch to work account"
|
|
188
188
|
echo -e " ${YELLOW}ccs personal${RESET} Switch to personal account"
|
|
189
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 ""
|
|
190
197
|
echo -e "${CYAN}Diagnostics:${RESET}"
|
|
191
198
|
echo -e " ${YELLOW}ccs doctor${RESET} Run health check and diagnostics"
|
|
192
199
|
echo ""
|
|
@@ -522,6 +529,33 @@ show_version() {
|
|
|
522
529
|
# Simple config display
|
|
523
530
|
local config="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
524
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
|
|
525
559
|
echo ""
|
|
526
560
|
|
|
527
561
|
echo -e "${CYAN}Documentation:${RESET} https://github.com/kaitranntt/ccs"
|
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 = "
|
|
15
|
+
$CcsVersion = "4.1.1"
|
|
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"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaitranntt/ccs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"lib/",
|
|
32
32
|
"scripts/",
|
|
33
33
|
"config/",
|
|
34
|
+
".claude/",
|
|
34
35
|
"VERSION",
|
|
35
36
|
"README.md",
|
|
36
37
|
"LICENSE"
|
package/scripts/postinstall.js
CHANGED
|
@@ -103,6 +103,28 @@ function createConfigFiles() {
|
|
|
103
103
|
}
|
|
104
104
|
console.log('');
|
|
105
105
|
|
|
106
|
+
// Copy .claude/ directory from package to ~/.ccs/.claude/ (v4.1.1)
|
|
107
|
+
try {
|
|
108
|
+
const ClaudeDirInstaller = require('../bin/utils/claude-dir-installer');
|
|
109
|
+
const installer = new ClaudeDirInstaller();
|
|
110
|
+
const packageDir = path.join(__dirname, '..');
|
|
111
|
+
installer.install(packageDir);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.warn('[!] Failed to install .claude/ directory:', err.message);
|
|
114
|
+
console.warn(' CCS items may not be available');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Install CCS items to ~/.claude/ (v4.1.0)
|
|
118
|
+
try {
|
|
119
|
+
const ClaudeSymlinkManager = require('../bin/utils/claude-symlink-manager');
|
|
120
|
+
const claudeSymlinkManager = new ClaudeSymlinkManager();
|
|
121
|
+
claudeSymlinkManager.install();
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.warn('[!] CCS item installation warning:', err.message);
|
|
124
|
+
console.warn(' Run "ccs update" to retry');
|
|
125
|
+
}
|
|
126
|
+
console.log('');
|
|
127
|
+
|
|
106
128
|
// Create config.json if missing
|
|
107
129
|
const configPath = path.join(ccsDir, 'config.json');
|
|
108
130
|
if (!fs.existsSync(configPath)) {
|