@kaitranntt/ccs 3.1.1 → 3.3.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.
- package/README.md +131 -128
- package/VERSION +1 -1
- package/bin/ccs.js +120 -4
- package/bin/glmt-proxy.js +307 -0
- package/bin/glmt-transformer.js +437 -0
- package/bin/shared-manager.js +175 -114
- package/config/base-glmt.settings.json +17 -0
- package/lib/ccs +2 -1
- package/lib/ccs.ps1 +2 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +109 -4
package/bin/shared-manager.js
CHANGED
|
@@ -6,33 +6,119 @@ const os = require('os');
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* SharedManager - Manages symlinked shared directories for CCS
|
|
9
|
-
*
|
|
9
|
+
* v3.2.0: Symlink-based architecture
|
|
10
10
|
*
|
|
11
|
-
* Purpose: Eliminates duplication
|
|
12
|
-
*
|
|
11
|
+
* Purpose: Eliminates duplication by symlinking:
|
|
12
|
+
* ~/.claude/ ← ~/.ccs/shared/ ← instance/
|
|
13
13
|
*/
|
|
14
14
|
class SharedManager {
|
|
15
15
|
constructor() {
|
|
16
16
|
this.homeDir = os.homedir();
|
|
17
17
|
this.sharedDir = path.join(this.homeDir, '.ccs', 'shared');
|
|
18
|
+
this.claudeDir = path.join(this.homeDir, '.claude');
|
|
18
19
|
this.instancesDir = path.join(this.homeDir, '.ccs', 'instances');
|
|
19
20
|
this.sharedDirs = ['commands', 'skills', 'agents'];
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
*
|
|
24
|
+
* Detect circular symlink before creation
|
|
25
|
+
* @param {string} target - Target path to link to
|
|
26
|
+
* @param {string} linkPath - Path where symlink will be created
|
|
27
|
+
* @returns {boolean} True if circular
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
_detectCircularSymlink(target, linkPath) {
|
|
31
|
+
// Check if target exists and is symlink
|
|
32
|
+
if (!fs.existsSync(target)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const stats = fs.lstatSync(target);
|
|
38
|
+
if (!stats.isSymbolicLink()) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Resolve target's link
|
|
43
|
+
const targetLink = fs.readlinkSync(target);
|
|
44
|
+
const resolvedTarget = path.resolve(path.dirname(target), targetLink);
|
|
45
|
+
|
|
46
|
+
// Check if target points back to our shared dir or link path
|
|
47
|
+
const sharedDir = path.join(this.homeDir, '.ccs', 'shared');
|
|
48
|
+
if (resolvedTarget.startsWith(sharedDir) || resolvedTarget === linkPath) {
|
|
49
|
+
console.log(`[!] Circular symlink detected: ${target} → ${resolvedTarget}`);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
// If can't read, assume not circular
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Ensure shared directories exist as symlinks to ~/.claude/
|
|
62
|
+
* Creates ~/.claude/ structure if missing
|
|
24
63
|
*/
|
|
25
64
|
ensureSharedDirectories() {
|
|
65
|
+
// Create ~/.claude/ if missing
|
|
66
|
+
if (!fs.existsSync(this.claudeDir)) {
|
|
67
|
+
console.log('[i] Creating ~/.claude/ directory structure');
|
|
68
|
+
fs.mkdirSync(this.claudeDir, { recursive: true, mode: 0o700 });
|
|
69
|
+
}
|
|
70
|
+
|
|
26
71
|
// Create shared directory
|
|
27
72
|
if (!fs.existsSync(this.sharedDir)) {
|
|
28
73
|
fs.mkdirSync(this.sharedDir, { recursive: true, mode: 0o700 });
|
|
29
74
|
}
|
|
30
75
|
|
|
31
|
-
// Create shared
|
|
76
|
+
// Create symlinks ~/.ccs/shared/* → ~/.claude/*
|
|
32
77
|
for (const dir of this.sharedDirs) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
78
|
+
const claudePath = path.join(this.claudeDir, dir);
|
|
79
|
+
const sharedPath = path.join(this.sharedDir, dir);
|
|
80
|
+
|
|
81
|
+
// Create directory in ~/.claude/ if missing
|
|
82
|
+
if (!fs.existsSync(claudePath)) {
|
|
83
|
+
fs.mkdirSync(claudePath, { recursive: true, mode: 0o700 });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check for circular symlink
|
|
87
|
+
if (this._detectCircularSymlink(claudePath, sharedPath)) {
|
|
88
|
+
console.log(`[!] Skipping ${dir}: circular symlink detected`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If already a symlink pointing to correct target, skip
|
|
93
|
+
if (fs.existsSync(sharedPath)) {
|
|
94
|
+
try {
|
|
95
|
+
const stats = fs.lstatSync(sharedPath);
|
|
96
|
+
if (stats.isSymbolicLink()) {
|
|
97
|
+
const currentTarget = fs.readlinkSync(sharedPath);
|
|
98
|
+
const resolvedTarget = path.resolve(path.dirname(sharedPath), currentTarget);
|
|
99
|
+
if (resolvedTarget === claudePath) {
|
|
100
|
+
continue; // Already correct
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
// Continue to recreate
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Remove existing directory/link
|
|
108
|
+
fs.rmSync(sharedPath, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create symlink
|
|
112
|
+
try {
|
|
113
|
+
fs.symlinkSync(claudePath, sharedPath, 'dir');
|
|
114
|
+
} catch (err) {
|
|
115
|
+
// Windows fallback: copy directory
|
|
116
|
+
if (process.platform === 'win32') {
|
|
117
|
+
this._copyDirectoryFallback(claudePath, sharedPath);
|
|
118
|
+
console.log(`[!] Symlink failed for ${dir}, copied instead (enable Developer Mode)`);
|
|
119
|
+
} else {
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
36
122
|
}
|
|
37
123
|
}
|
|
38
124
|
}
|
|
@@ -57,9 +143,9 @@ class SharedManager {
|
|
|
57
143
|
try {
|
|
58
144
|
fs.symlinkSync(targetPath, linkPath, 'dir');
|
|
59
145
|
} catch (err) {
|
|
60
|
-
// Windows fallback
|
|
146
|
+
// Windows fallback
|
|
61
147
|
if (process.platform === 'win32') {
|
|
62
|
-
this.
|
|
148
|
+
this._copyDirectoryFallback(targetPath, linkPath);
|
|
63
149
|
console.log(`[!] Symlink failed for ${dir}, copied instead (enable Developer Mode)`);
|
|
64
150
|
} else {
|
|
65
151
|
throw err;
|
|
@@ -69,155 +155,130 @@ class SharedManager {
|
|
|
69
155
|
}
|
|
70
156
|
|
|
71
157
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @private
|
|
158
|
+
* Migrate from v3.1.1 (copied data in ~/.ccs/shared/) to v3.2.0 (symlinks to ~/.claude/)
|
|
159
|
+
* Runs once on upgrade
|
|
75
160
|
*/
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check if ALL shared directories are empty
|
|
83
|
-
const allEmpty = this.sharedDirs.every(dir => {
|
|
84
|
-
const dirPath = path.join(this.sharedDir, dir);
|
|
85
|
-
if (!fs.existsSync(dirPath)) return true;
|
|
161
|
+
migrateFromV311() {
|
|
162
|
+
// Check if migration already done (shared dirs are symlinks)
|
|
163
|
+
const commandsPath = path.join(this.sharedDir, 'commands');
|
|
164
|
+
if (fs.existsSync(commandsPath)) {
|
|
86
165
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
166
|
+
if (fs.lstatSync(commandsPath).isSymbolicLink()) {
|
|
167
|
+
return; // Already migrated
|
|
168
|
+
}
|
|
89
169
|
} catch (err) {
|
|
90
|
-
|
|
170
|
+
// Continue with migration
|
|
91
171
|
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return allEmpty;
|
|
95
|
-
}
|
|
172
|
+
}
|
|
96
173
|
|
|
97
|
-
|
|
98
|
-
* Perform migration from ~/.claude/ to ~/.ccs/shared/
|
|
99
|
-
* @returns {object} { commands: N, skills: N, agents: N }
|
|
100
|
-
* @private
|
|
101
|
-
*/
|
|
102
|
-
_performMigration() {
|
|
103
|
-
const stats = { commands: 0, skills: 0, agents: 0 };
|
|
104
|
-
const claudeDir = path.join(this.homeDir, '.claude');
|
|
174
|
+
console.log('[i] Migrating from v3.1.1 to v3.2.0...');
|
|
105
175
|
|
|
106
|
-
|
|
107
|
-
|
|
176
|
+
// Ensure ~/.claude/ exists
|
|
177
|
+
if (!fs.existsSync(this.claudeDir)) {
|
|
178
|
+
fs.mkdirSync(this.claudeDir, { recursive: true, mode: 0o700 });
|
|
108
179
|
}
|
|
109
180
|
|
|
110
|
-
//
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
stats.commands = result.copied;
|
|
115
|
-
}
|
|
181
|
+
// Copy user modifications from ~/.ccs/shared/ to ~/.claude/
|
|
182
|
+
for (const dir of this.sharedDirs) {
|
|
183
|
+
const sharedPath = path.join(this.sharedDir, dir);
|
|
184
|
+
const claudePath = path.join(this.claudeDir, dir);
|
|
116
185
|
|
|
117
|
-
|
|
118
|
-
const skillsPath = path.join(claudeDir, 'skills');
|
|
119
|
-
if (fs.existsSync(skillsPath)) {
|
|
120
|
-
const result = this._copyDirectory(skillsPath, path.join(this.sharedDir, 'skills'));
|
|
121
|
-
stats.skills = result.copied;
|
|
122
|
-
}
|
|
186
|
+
if (!fs.existsSync(sharedPath)) continue;
|
|
123
187
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
188
|
+
try {
|
|
189
|
+
const stats = fs.lstatSync(sharedPath);
|
|
190
|
+
if (!stats.isDirectory()) continue;
|
|
191
|
+
} catch (err) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
130
194
|
|
|
131
|
-
|
|
132
|
-
|
|
195
|
+
// Create claude dir if missing
|
|
196
|
+
if (!fs.existsSync(claudePath)) {
|
|
197
|
+
fs.mkdirSync(claudePath, { recursive: true, mode: 0o700 });
|
|
198
|
+
}
|
|
133
199
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
200
|
+
// Copy files from shared to claude (preserve user modifications)
|
|
201
|
+
try {
|
|
202
|
+
const entries = fs.readdirSync(sharedPath, { withFileTypes: true });
|
|
203
|
+
let copied = 0;
|
|
204
|
+
|
|
205
|
+
for (const entry of entries) {
|
|
206
|
+
const src = path.join(sharedPath, entry.name);
|
|
207
|
+
const dest = path.join(claudePath, entry.name);
|
|
208
|
+
|
|
209
|
+
// Skip if already exists in claude
|
|
210
|
+
if (fs.existsSync(dest)) continue;
|
|
211
|
+
|
|
212
|
+
if (entry.isDirectory()) {
|
|
213
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
214
|
+
} else {
|
|
215
|
+
fs.copyFileSync(src, dest);
|
|
216
|
+
}
|
|
217
|
+
copied++;
|
|
218
|
+
}
|
|
140
219
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
220
|
+
if (copied > 0) {
|
|
221
|
+
console.log(`[OK] Migrated ${copied} ${dir} to ~/.claude/${dir}`);
|
|
222
|
+
}
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.log(`[!] Failed to migrate ${dir}: ${err.message}`);
|
|
225
|
+
}
|
|
145
226
|
}
|
|
146
227
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// Create shared directories
|
|
228
|
+
// Now run ensureSharedDirectories to create symlinks
|
|
150
229
|
this.ensureSharedDirectories();
|
|
151
230
|
|
|
152
|
-
//
|
|
153
|
-
const stats = this._performMigration();
|
|
154
|
-
|
|
155
|
-
// Show results
|
|
156
|
-
const total = stats.commands + stats.skills + stats.agents;
|
|
157
|
-
if (total === 0) {
|
|
158
|
-
console.log('[OK] No content to migrate (empty ~/.claude/)');
|
|
159
|
-
} else {
|
|
160
|
-
const parts = [];
|
|
161
|
-
if (stats.commands > 0) parts.push(`${stats.commands} commands`);
|
|
162
|
-
if (stats.skills > 0) parts.push(`${stats.skills} skills`);
|
|
163
|
-
if (stats.agents > 0) parts.push(`${stats.agents} agents`);
|
|
164
|
-
console.log(`[OK] Migrated ${parts.join(', ')}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Update all instances to use symlinks
|
|
231
|
+
// Update all instances to use new symlinks
|
|
168
232
|
if (fs.existsSync(this.instancesDir)) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
233
|
+
try {
|
|
234
|
+
const instances = fs.readdirSync(this.instancesDir);
|
|
235
|
+
|
|
236
|
+
for (const instance of instances) {
|
|
237
|
+
const instancePath = path.join(this.instancesDir, instance);
|
|
238
|
+
try {
|
|
239
|
+
if (fs.statSync(instancePath).isDirectory()) {
|
|
240
|
+
this.linkSharedDirectories(instancePath);
|
|
241
|
+
}
|
|
242
|
+
} catch (err) {
|
|
243
|
+
console.log(`[!] Failed to update instance ${instance}: ${err.message}`);
|
|
244
|
+
}
|
|
175
245
|
}
|
|
246
|
+
} catch (err) {
|
|
247
|
+
// No instances to update
|
|
176
248
|
}
|
|
177
249
|
}
|
|
250
|
+
|
|
251
|
+
console.log('[OK] Migration to v3.2.0 complete');
|
|
178
252
|
}
|
|
179
253
|
|
|
180
254
|
/**
|
|
181
|
-
* Copy directory
|
|
255
|
+
* Copy directory as fallback (Windows without Developer Mode)
|
|
182
256
|
* @param {string} src - Source directory
|
|
183
257
|
* @param {string} dest - Destination directory
|
|
184
|
-
* @returns {object} { copied: N, skipped: N }
|
|
185
258
|
* @private
|
|
186
259
|
*/
|
|
187
|
-
|
|
260
|
+
_copyDirectoryFallback(src, dest) {
|
|
188
261
|
if (!fs.existsSync(src)) {
|
|
189
|
-
|
|
262
|
+
fs.mkdirSync(src, { recursive: true, mode: 0o700 });
|
|
263
|
+
return;
|
|
190
264
|
}
|
|
191
265
|
|
|
192
266
|
if (!fs.existsSync(dest)) {
|
|
193
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
267
|
+
fs.mkdirSync(dest, { recursive: true, mode: 0o700 });
|
|
194
268
|
}
|
|
195
269
|
|
|
196
270
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
197
|
-
let copied = 0;
|
|
198
|
-
let skipped = 0;
|
|
199
271
|
|
|
200
272
|
for (const entry of entries) {
|
|
201
273
|
const srcPath = path.join(src, entry.name);
|
|
202
274
|
const destPath = path.join(dest, entry.name);
|
|
203
275
|
|
|
204
|
-
// SAFETY: Skip if destination exists (preserve user modifications)
|
|
205
|
-
if (fs.existsSync(destPath)) {
|
|
206
|
-
skipped++;
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
276
|
if (entry.isDirectory()) {
|
|
211
|
-
|
|
212
|
-
copied += stats.copied;
|
|
213
|
-
skipped += stats.skipped;
|
|
277
|
+
this._copyDirectoryFallback(srcPath, destPath);
|
|
214
278
|
} else {
|
|
215
279
|
fs.copyFileSync(srcPath, destPath);
|
|
216
|
-
copied++;
|
|
217
280
|
}
|
|
218
281
|
}
|
|
219
|
-
|
|
220
|
-
return { copied, skipped };
|
|
221
282
|
}
|
|
222
283
|
}
|
|
223
284
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"ANTHROPIC_BASE_URL": "https://api.z.ai/api/coding/paas/v4/chat/completions",
|
|
4
|
+
"ANTHROPIC_AUTH_TOKEN": "YOUR_GLM_API_KEY_HERE",
|
|
5
|
+
"ANTHROPIC_MODEL": "glm-4.6",
|
|
6
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-4.6",
|
|
7
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-4.6",
|
|
8
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-4.6",
|
|
9
|
+
"ANTHROPIC_TEMPERATURE": "0.2",
|
|
10
|
+
"ANTHROPIC_MAX_TOKENS": "65536",
|
|
11
|
+
"MAX_THINKING_TOKENS": "32768",
|
|
12
|
+
"ENABLE_STREAMING": "true",
|
|
13
|
+
"ANTHROPIC_SAFE_MODE": "false",
|
|
14
|
+
"API_TIMEOUT_MS": "3000000"
|
|
15
|
+
},
|
|
16
|
+
"alwaysThinkingEnabled": true
|
|
17
|
+
}
|
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
|
+
CCS_VERSION="3.3.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"
|
|
@@ -51,6 +51,7 @@ show_help() {
|
|
|
51
51
|
echo -e "${CYAN}Model Switching:${RESET}"
|
|
52
52
|
echo -e " ${YELLOW}ccs${RESET} Use default Claude account"
|
|
53
53
|
echo -e " ${YELLOW}ccs glm${RESET} Switch to GLM 4.6 model"
|
|
54
|
+
echo -e " ${YELLOW}ccs glmt${RESET} Switch to GLM with thinking mode"
|
|
54
55
|
echo -e " ${YELLOW}ccs kimi${RESET} Switch to Kimi for Coding"
|
|
55
56
|
echo -e " ${YELLOW}ccs glm${RESET} \"debug this code\" Use GLM and run command"
|
|
56
57
|
echo ""
|
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.
|
|
15
|
+
$CcsVersion = "3.3.0"
|
|
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"
|
|
@@ -107,6 +107,7 @@ function Show-Help {
|
|
|
107
107
|
Write-ColorLine "Model Switching:" "Cyan"
|
|
108
108
|
Write-ColorLine " ccs Use default Claude account" "Yellow"
|
|
109
109
|
Write-ColorLine " ccs glm Switch to GLM 4.6 model" "Yellow"
|
|
110
|
+
Write-ColorLine " ccs glmt Switch to GLM with thinking mode" "Yellow"
|
|
110
111
|
Write-ColorLine " ccs kimi Switch to Kimi for Coding" "Yellow"
|
|
111
112
|
Write-ColorLine " ccs glm 'debug this code' Use GLM and run command" "Yellow"
|
|
112
113
|
Write-Host ""
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -33,6 +33,7 @@ function validateConfiguration() {
|
|
|
33
33
|
const requiredFiles = [
|
|
34
34
|
{ path: path.join(ccsDir, 'config.json'), name: 'config.json' },
|
|
35
35
|
{ path: path.join(ccsDir, 'glm.settings.json'), name: 'glm.settings.json' },
|
|
36
|
+
{ path: path.join(ccsDir, 'glmt.settings.json'), name: 'glmt.settings.json' },
|
|
36
37
|
{ path: path.join(ccsDir, 'kimi.settings.json'), name: 'kimi.settings.json' }
|
|
37
38
|
];
|
|
38
39
|
|
|
@@ -89,15 +90,16 @@ function createConfigFiles() {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
// Migrate from
|
|
93
|
+
// Migrate from v3.1.1 to v3.2.0 (symlink architecture)
|
|
93
94
|
console.log('');
|
|
94
95
|
try {
|
|
95
96
|
const SharedManager = require('../bin/shared-manager');
|
|
96
97
|
const sharedManager = new SharedManager();
|
|
97
|
-
sharedManager.
|
|
98
|
+
sharedManager.migrateFromV311();
|
|
99
|
+
sharedManager.ensureSharedDirectories();
|
|
98
100
|
} catch (err) {
|
|
99
101
|
console.warn('[!] Migration warning:', err.message);
|
|
100
|
-
console.warn('
|
|
102
|
+
console.warn(' Migration will retry on next run');
|
|
101
103
|
}
|
|
102
104
|
console.log('');
|
|
103
105
|
|
|
@@ -107,6 +109,7 @@ function createConfigFiles() {
|
|
|
107
109
|
const config = {
|
|
108
110
|
profiles: {
|
|
109
111
|
glm: '~/.ccs/glm.settings.json',
|
|
112
|
+
glmt: '~/.ccs/glmt.settings.json',
|
|
110
113
|
kimi: '~/.ccs/kimi.settings.json',
|
|
111
114
|
default: '~/.claude/settings.json'
|
|
112
115
|
}
|
|
@@ -119,7 +122,21 @@ function createConfigFiles() {
|
|
|
119
122
|
|
|
120
123
|
console.log('[OK] Created config: ~/.ccs/config.json');
|
|
121
124
|
} else {
|
|
122
|
-
|
|
125
|
+
// Update existing config with glmt if missing (migration for v3.x users)
|
|
126
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
127
|
+
// Ensure profiles object exists
|
|
128
|
+
if (!config.profiles) {
|
|
129
|
+
config.profiles = {};
|
|
130
|
+
}
|
|
131
|
+
if (!config.profiles.glmt) {
|
|
132
|
+
config.profiles.glmt = '~/.ccs/glmt.settings.json';
|
|
133
|
+
const tmpPath = `${configPath}.tmp`;
|
|
134
|
+
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
135
|
+
fs.renameSync(tmpPath, configPath);
|
|
136
|
+
console.log('[OK] Updated config with GLMT profile');
|
|
137
|
+
} else {
|
|
138
|
+
console.log('[OK] Config exists: ~/.ccs/config.json (preserved)');
|
|
139
|
+
}
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
// Create glm.settings.json if missing
|
|
@@ -151,6 +168,94 @@ function createConfigFiles() {
|
|
|
151
168
|
console.log('[OK] GLM profile exists: ~/.ccs/glm.settings.json (preserved)');
|
|
152
169
|
}
|
|
153
170
|
|
|
171
|
+
// Create glmt.settings.json if missing
|
|
172
|
+
const glmtSettingsPath = path.join(ccsDir, 'glmt.settings.json');
|
|
173
|
+
if (!fs.existsSync(glmtSettingsPath)) {
|
|
174
|
+
const glmtSettings = {
|
|
175
|
+
env: {
|
|
176
|
+
ANTHROPIC_BASE_URL: 'https://api.z.ai/api/coding/paas/v4/chat/completions',
|
|
177
|
+
ANTHROPIC_AUTH_TOKEN: 'YOUR_GLM_API_KEY_HERE',
|
|
178
|
+
ANTHROPIC_MODEL: 'glm-4.6',
|
|
179
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: 'glm-4.6',
|
|
180
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: 'glm-4.6',
|
|
181
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'glm-4.6',
|
|
182
|
+
ANTHROPIC_TEMPERATURE: '0.2',
|
|
183
|
+
ANTHROPIC_MAX_TOKENS: '65536',
|
|
184
|
+
MAX_THINKING_TOKENS: '32768',
|
|
185
|
+
ENABLE_STREAMING: 'true',
|
|
186
|
+
ANTHROPIC_SAFE_MODE: 'false',
|
|
187
|
+
API_TIMEOUT_MS: '3000000'
|
|
188
|
+
},
|
|
189
|
+
alwaysThinkingEnabled: true
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Atomic write
|
|
193
|
+
const tmpPath = `${glmtSettingsPath}.tmp`;
|
|
194
|
+
fs.writeFileSync(tmpPath, JSON.stringify(glmtSettings, null, 2) + '\n', 'utf8');
|
|
195
|
+
fs.renameSync(tmpPath, glmtSettingsPath);
|
|
196
|
+
|
|
197
|
+
console.log('[OK] Created GLMT profile: ~/.ccs/glmt.settings.json');
|
|
198
|
+
console.log('');
|
|
199
|
+
console.log(' [!] Configure GLMT API key:');
|
|
200
|
+
console.log(' 1. Get key from: https://api.z.ai');
|
|
201
|
+
console.log(' 2. Edit: ~/.ccs/glmt.settings.json');
|
|
202
|
+
console.log(' 3. Replace: YOUR_GLM_API_KEY_HERE');
|
|
203
|
+
console.log(' Note: GLMT enables GLM thinking mode (reasoning)');
|
|
204
|
+
console.log(' Defaults: Temperature 0.2, thinking enabled, 50min timeout');
|
|
205
|
+
} else {
|
|
206
|
+
console.log('[OK] GLMT profile exists: ~/.ccs/glmt.settings.json (preserved)');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Migrate existing GLMT configs to include new defaults (v3.3.0)
|
|
210
|
+
if (fs.existsSync(glmtSettingsPath)) {
|
|
211
|
+
try {
|
|
212
|
+
const existing = JSON.parse(fs.readFileSync(glmtSettingsPath, 'utf8'));
|
|
213
|
+
let updated = false;
|
|
214
|
+
|
|
215
|
+
// Ensure env object exists
|
|
216
|
+
if (!existing.env) {
|
|
217
|
+
existing.env = {};
|
|
218
|
+
updated = true;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Add missing env vars (preserve existing values)
|
|
222
|
+
const envDefaults = {
|
|
223
|
+
ANTHROPIC_TEMPERATURE: '0.2',
|
|
224
|
+
ANTHROPIC_MAX_TOKENS: '65536',
|
|
225
|
+
MAX_THINKING_TOKENS: '32768',
|
|
226
|
+
ENABLE_STREAMING: 'true',
|
|
227
|
+
ANTHROPIC_SAFE_MODE: 'false',
|
|
228
|
+
API_TIMEOUT_MS: '3000000'
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
for (const [key, value] of Object.entries(envDefaults)) {
|
|
232
|
+
if (existing.env[key] === undefined) {
|
|
233
|
+
existing.env[key] = value;
|
|
234
|
+
updated = true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Add alwaysThinkingEnabled if missing
|
|
239
|
+
if (existing.alwaysThinkingEnabled === undefined) {
|
|
240
|
+
existing.alwaysThinkingEnabled = true;
|
|
241
|
+
updated = true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Write back if updated
|
|
245
|
+
if (updated) {
|
|
246
|
+
const tmpPath = `${glmtSettingsPath}.tmp`;
|
|
247
|
+
fs.writeFileSync(tmpPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');
|
|
248
|
+
fs.renameSync(tmpPath, glmtSettingsPath);
|
|
249
|
+
console.log('[OK] Migrated GLMT config with new defaults (v3.3.0)');
|
|
250
|
+
console.log(' Added: temperature, max_tokens, thinking settings, alwaysThinkingEnabled');
|
|
251
|
+
}
|
|
252
|
+
} catch (err) {
|
|
253
|
+
console.warn('[!] GLMT config migration failed:', err.message);
|
|
254
|
+
console.warn(' Existing config preserved, may be missing new defaults');
|
|
255
|
+
console.warn(' You can manually add fields or delete file to regenerate');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
154
259
|
// Create kimi.settings.json if missing
|
|
155
260
|
const kimiSettingsPath = path.join(ccsDir, 'kimi.settings.json');
|
|
156
261
|
if (!fs.existsSync(kimiSettingsPath)) {
|