@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.
@@ -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="3.5.0"
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 = "3.5.0"
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.5.0",
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"
@@ -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)) {