@kaitranntt/ccs 4.1.0 → 4.1.2

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 CHANGED
@@ -1 +1 @@
1
- 4.1.0
1
+ 4.1.2
package/bin/ccs.js CHANGED
@@ -255,6 +255,12 @@ async function handleDoctorCommand() {
255
255
  }
256
256
 
257
257
  async function handleUpdateCommand() {
258
+ // First, copy .claude/ directory from package to ~/.ccs/.claude/
259
+ const ClaudeDirInstaller = require('./utils/claude-dir-installer');
260
+ const installer = new ClaudeDirInstaller();
261
+ installer.install();
262
+
263
+ // Then, create symlinks from ~/.ccs/.claude/ to ~/.claude/
258
264
  const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
259
265
  const manager = new ClaudeSymlinkManager();
260
266
 
@@ -88,7 +88,8 @@ class HeadlessExecutor {
88
88
  if (lastSession) {
89
89
  args.push('--resume', lastSession.sessionId);
90
90
  if (process.env.CCS_DEBUG) {
91
- console.error(`[i] Resuming session: ${lastSession.sessionId} (${lastSession.turns} turns, $${lastSession.totalCost.toFixed(4)})`);
91
+ const cost = lastSession.totalCost !== undefined && lastSession.totalCost !== null ? lastSession.totalCost.toFixed(4) : '0.0000';
92
+ console.error(`[i] Resuming session: ${lastSession.sessionId} (${lastSession.turns} turns, $${cost})`);
92
93
  }
93
94
  } else if (sessionId) {
94
95
  args.push('--resume', sessionId);
@@ -437,7 +437,9 @@ class ResultFormatter {
437
437
  // Abbreviate session ID (Git-style first 8 chars)
438
438
  const shortId = sessionId && sessionId.length > 8 ? sessionId.substring(0, 8) : sessionId;
439
439
  output += `[i] Session persisted with ID: ${shortId}\n`;
440
- output += `[i] Cost: $${totalCost.toFixed(4)}\n`;
440
+ if (totalCost !== undefined && totalCost !== null) {
441
+ output += `[i] Cost: $${totalCost.toFixed(4)}\n`;
442
+ }
441
443
 
442
444
  return output;
443
445
  }
@@ -60,7 +60,8 @@ class SessionManager {
60
60
  this._saveSessions(sessions);
61
61
 
62
62
  if (process.env.CCS_DEBUG) {
63
- console.error(`[i] Updated session: ${sessionId}, total: $${sessions[key].totalCost.toFixed(4)}, turns: ${sessions[key].turns}`);
63
+ const cost = sessions[key].totalCost !== undefined && sessions[key].totalCost !== null ? sessions[key].totalCost.toFixed(4) : '0.0000';
64
+ console.error(`[i] Updated session: ${sessionId}, total: $${cost}, turns: ${sessions[key].turns}`);
64
65
  }
65
66
  }
66
67
  }
@@ -0,0 +1,102 @@
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
+ * ClaudeDirInstaller - Manages copying .claude/ directory from package to ~/.ccs/.claude/
10
+ * v4.1.1: Fix for npm install not copying .claude/ directory
11
+ */
12
+ class ClaudeDirInstaller {
13
+ constructor() {
14
+ this.homeDir = os.homedir();
15
+ this.ccsClaudeDir = path.join(this.homeDir, '.ccs', '.claude');
16
+ }
17
+
18
+ /**
19
+ * Copy .claude/ directory from package to ~/.ccs/.claude/
20
+ * @param {string} packageDir - Package installation directory (default: auto-detect)
21
+ */
22
+ install(packageDir) {
23
+ try {
24
+ // Auto-detect package directory if not provided
25
+ if (!packageDir) {
26
+ // Try to find package root by going up from this file
27
+ packageDir = path.join(__dirname, '..', '..');
28
+ }
29
+
30
+ const packageClaudeDir = path.join(packageDir, '.claude');
31
+
32
+ if (!fs.existsSync(packageClaudeDir)) {
33
+ console.log('[!] Package .claude/ directory not found');
34
+ console.log(` Searched in: ${packageClaudeDir}`);
35
+ console.log(' This may be a development installation');
36
+ return false;
37
+ }
38
+
39
+ console.log('[i] Installing CCS .claude/ items...');
40
+
41
+ // Remove old version before copying new one
42
+ if (fs.existsSync(this.ccsClaudeDir)) {
43
+ fs.rmSync(this.ccsClaudeDir, { recursive: true, force: true });
44
+ }
45
+
46
+ // Use fs.cpSync for recursive copy (Node.js 16.7.0+)
47
+ // Fallback to manual copy for older Node.js versions
48
+ if (fs.cpSync) {
49
+ fs.cpSync(packageClaudeDir, this.ccsClaudeDir, { recursive: true });
50
+ } else {
51
+ // Fallback for Node.js < 16.7.0
52
+ this._copyDirRecursive(packageClaudeDir, this.ccsClaudeDir);
53
+ }
54
+
55
+ console.log('[OK] Copied .claude/ items to ~/.ccs/.claude/');
56
+ return true;
57
+ } catch (err) {
58
+ console.warn('[!] Failed to copy .claude/ directory:', err.message);
59
+ console.warn(' CCS items may not be available');
60
+ return false;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Recursively copy directory (fallback for Node.js < 16.7.0)
66
+ * @param {string} src - Source directory
67
+ * @param {string} dest - Destination directory
68
+ * @private
69
+ */
70
+ _copyDirRecursive(src, dest) {
71
+ // Create destination directory
72
+ if (!fs.existsSync(dest)) {
73
+ fs.mkdirSync(dest, { recursive: true });
74
+ }
75
+
76
+ // Read source directory
77
+ const entries = fs.readdirSync(src, { withFileTypes: true });
78
+
79
+ for (const entry of entries) {
80
+ const srcPath = path.join(src, entry.name);
81
+ const destPath = path.join(dest, entry.name);
82
+
83
+ if (entry.isDirectory()) {
84
+ // Recursively copy subdirectory
85
+ this._copyDirRecursive(srcPath, destPath);
86
+ } else {
87
+ // Copy file
88
+ fs.copyFileSync(srcPath, destPath);
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Check if ~/.ccs/.claude/ exists and is valid
95
+ * @returns {boolean} True if directory exists
96
+ */
97
+ isInstalled() {
98
+ return fs.existsSync(this.ccsClaudeDir);
99
+ }
100
+ }
101
+
102
+ module.exports = ClaudeDirInstaller;
@@ -1,12 +1,7 @@
1
1
  {
2
2
  "env": {
3
3
  "ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/",
4
- "ANTHROPIC_AUTH_TOKEN": "YOUR_KIMI_API_KEY_HERE",
5
- "ANTHROPIC_MODEL": "kimi-for-coding",
6
- "ANTHROPIC_SMALL_FAST_MODEL": "kimi-for-coding",
7
- "ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-for-coding",
8
- "ANTHROPIC_DEFAULT_SONNET_MODEL": "kimi-for-coding",
9
- "ANTHROPIC_DEFAULT_HAIKU_MODEL": "kimi-for-coding"
4
+ "ANTHROPIC_AUTH_TOKEN": "YOUR_KIMI_API_KEY_HERE"
10
5
  },
11
6
  "alwaysThinkingEnabled": true
12
7
  }
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.0"
5
+ CCS_VERSION="4.1.2"
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"
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.0"
15
+ $CcsVersion = "4.1.2"
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": "4.1.0",
3
+ "version": "4.1.2",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
5
5
  "keywords": [
6
6
  "cli",
@@ -103,6 +103,17 @@ 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
+
106
117
  // Install CCS items to ~/.claude/ (v4.1.0)
107
118
  try {
108
119
  const ClaudeSymlinkManager = require('../bin/utils/claude-symlink-manager');
@@ -273,12 +284,7 @@ function createConfigFiles() {
273
284
  const kimiSettings = {
274
285
  env: {
275
286
  ANTHROPIC_BASE_URL: 'https://api.kimi.com/coding/',
276
- ANTHROPIC_AUTH_TOKEN: 'YOUR_KIMI_API_KEY_HERE',
277
- ANTHROPIC_MODEL: 'kimi-for-coding',
278
- ANTHROPIC_SMALL_FAST_MODEL: 'kimi-for-coding',
279
- ANTHROPIC_DEFAULT_OPUS_MODEL: 'kimi-for-coding',
280
- ANTHROPIC_DEFAULT_SONNET_MODEL: 'kimi-for-coding',
281
- ANTHROPIC_DEFAULT_HAIKU_MODEL: 'kimi-for-coding'
287
+ ANTHROPIC_AUTH_TOKEN: 'YOUR_KIMI_API_KEY_HERE'
282
288
  },
283
289
  alwaysThinkingEnabled: true
284
290
  };
@@ -298,6 +304,62 @@ function createConfigFiles() {
298
304
  console.log('[OK] Kimi profile exists: ~/.ccs/kimi.settings.json (preserved)');
299
305
  }
300
306
 
307
+ // Migrate existing Kimi configs to remove deprecated model fields (v4.1.2)
308
+ // Kimi API changed - model fields now cause 401 errors
309
+ if (fs.existsSync(kimiSettingsPath)) {
310
+ try {
311
+ const existing = JSON.parse(fs.readFileSync(kimiSettingsPath, 'utf8'));
312
+ let updated = false;
313
+
314
+ // Ensure env object exists
315
+ if (!existing.env) {
316
+ existing.env = {};
317
+ updated = true;
318
+ }
319
+
320
+ // Remove deprecated model fields that cause 401 errors
321
+ const deprecatedFields = [
322
+ 'ANTHROPIC_MODEL',
323
+ 'ANTHROPIC_SMALL_FAST_MODEL',
324
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
325
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
326
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL'
327
+ ];
328
+
329
+ for (const field of deprecatedFields) {
330
+ if (existing.env[field] !== undefined) {
331
+ delete existing.env[field];
332
+ updated = true;
333
+ }
334
+ }
335
+
336
+ // Ensure required fields exist
337
+ if (!existing.env.ANTHROPIC_BASE_URL) {
338
+ existing.env.ANTHROPIC_BASE_URL = 'https://api.kimi.com/coding/';
339
+ updated = true;
340
+ }
341
+
342
+ // Add alwaysThinkingEnabled if missing
343
+ if (existing.alwaysThinkingEnabled === undefined) {
344
+ existing.alwaysThinkingEnabled = true;
345
+ updated = true;
346
+ }
347
+
348
+ // Write back if updated
349
+ if (updated) {
350
+ const tmpPath = `${kimiSettingsPath}.tmp`;
351
+ fs.writeFileSync(tmpPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');
352
+ fs.renameSync(tmpPath, kimiSettingsPath);
353
+ console.log('[OK] Migrated Kimi config (v4.1.2): removed deprecated model fields');
354
+ console.log(' Kimi API no longer requires model fields (they cause 401 errors)');
355
+ }
356
+ } catch (err) {
357
+ console.warn('[!] Kimi config migration failed:', err.message);
358
+ console.warn(' Existing config preserved, but may cause 401 errors');
359
+ console.warn(' Manually remove ANTHROPIC_MODEL fields from ~/.ccs/kimi.settings.json');
360
+ }
361
+ }
362
+
301
363
  // Copy shell completion files to ~/.ccs/completions/
302
364
  const completionsDir = path.join(ccsDir, 'completions');
303
365
  const scriptsCompletionDir = path.join(__dirname, '../scripts/completion');