@kaitranntt/ccs 2.4.1 → 2.4.3
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/ccs.js +79 -4
- package/bin/claude-detector.js +48 -4
- package/lib/ccs +1 -1
- package/lib/ccs.ps1 +1 -1
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.4.
|
|
1
|
+
2.4.3
|
package/bin/ccs.js
CHANGED
|
@@ -11,6 +11,30 @@ const { getSettingsPath } = require('./config-manager');
|
|
|
11
11
|
// Version (sync with package.json)
|
|
12
12
|
const CCS_VERSION = require('../package.json').version;
|
|
13
13
|
|
|
14
|
+
// Helper: Get spawn options for claude execution
|
|
15
|
+
// On Windows, .cmd/.bat/.ps1 files need shell: true
|
|
16
|
+
function getSpawnOptions(claudePath) {
|
|
17
|
+
const isWindows = process.platform === 'win32';
|
|
18
|
+
const needsShell = isWindows && /\.(cmd|bat|ps1)$/i.test(claudePath);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
stdio: 'inherit',
|
|
22
|
+
shell: needsShell,
|
|
23
|
+
windowsHide: true // Hide the console window on Windows
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper: Escape arguments for shell execution to prevent security vulnerabilities
|
|
28
|
+
function escapeShellArg(arg) {
|
|
29
|
+
if (process.platform !== 'win32') {
|
|
30
|
+
// Unix-like systems: escape single quotes and wrap in single quotes
|
|
31
|
+
return "'" + arg.replace(/'/g, "'\"'\"'") + "'";
|
|
32
|
+
} else {
|
|
33
|
+
// Windows: escape double quotes and wrap in double quotes
|
|
34
|
+
return '"' + arg.replace(/"/g, '""') + '"';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
14
38
|
// Special command handlers
|
|
15
39
|
function handleVersionCommand() {
|
|
16
40
|
console.log(`CCS (Claude Code Switch) version ${CCS_VERSION}`);
|
|
@@ -28,8 +52,25 @@ function handleVersionCommand() {
|
|
|
28
52
|
function handleHelpCommand(remainingArgs) {
|
|
29
53
|
const claudeCli = detectClaudeCli();
|
|
30
54
|
|
|
55
|
+
// Check if claude was found
|
|
56
|
+
if (!claudeCli) {
|
|
57
|
+
showClaudeNotFoundError();
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
31
61
|
// Execute claude --help
|
|
32
|
-
const
|
|
62
|
+
const spawnOpts = getSpawnOptions(claudeCli);
|
|
63
|
+
let claudeArgs, child;
|
|
64
|
+
|
|
65
|
+
if (spawnOpts.shell) {
|
|
66
|
+
// When shell is required, escape arguments properly
|
|
67
|
+
claudeArgs = [claudeCli, '--help', ...remainingArgs].map(escapeShellArg).join(' ');
|
|
68
|
+
child = spawn(claudeArgs, spawnOpts);
|
|
69
|
+
} else {
|
|
70
|
+
// When no shell needed, use arguments array directly
|
|
71
|
+
claudeArgs = ['--help', ...remainingArgs];
|
|
72
|
+
child = spawn(claudeCli, claudeArgs, spawnOpts);
|
|
73
|
+
}
|
|
33
74
|
|
|
34
75
|
child.on('exit', (code, signal) => {
|
|
35
76
|
if (signal) {
|
|
@@ -111,8 +152,25 @@ function main() {
|
|
|
111
152
|
if (profile === 'default') {
|
|
112
153
|
const claudeCli = detectClaudeCli();
|
|
113
154
|
|
|
155
|
+
// Check if claude was found
|
|
156
|
+
if (!claudeCli) {
|
|
157
|
+
showClaudeNotFoundError();
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
114
161
|
// Execute claude with args
|
|
115
|
-
const
|
|
162
|
+
const spawnOpts = getSpawnOptions(claudeCli);
|
|
163
|
+
let claudeArgs, child;
|
|
164
|
+
|
|
165
|
+
if (spawnOpts.shell) {
|
|
166
|
+
// When shell is required, escape arguments properly
|
|
167
|
+
claudeArgs = [claudeCli, ...remainingArgs].map(escapeShellArg).join(' ');
|
|
168
|
+
child = spawn(claudeArgs, spawnOpts);
|
|
169
|
+
} else {
|
|
170
|
+
// When no shell needed, use arguments array directly
|
|
171
|
+
claudeArgs = remainingArgs;
|
|
172
|
+
child = spawn(claudeCli, claudeArgs, spawnOpts);
|
|
173
|
+
}
|
|
116
174
|
|
|
117
175
|
child.on('exit', (code, signal) => {
|
|
118
176
|
if (signal) {
|
|
@@ -136,9 +194,26 @@ function main() {
|
|
|
136
194
|
// Detect Claude CLI
|
|
137
195
|
const claudeCli = detectClaudeCli();
|
|
138
196
|
|
|
197
|
+
// Check if claude was found
|
|
198
|
+
if (!claudeCli) {
|
|
199
|
+
showClaudeNotFoundError();
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
139
203
|
// Execute claude with --settings
|
|
140
|
-
const
|
|
141
|
-
const
|
|
204
|
+
const claudeArgsList = ['--settings', settingsPath, ...remainingArgs];
|
|
205
|
+
const spawnOpts = getSpawnOptions(claudeCli);
|
|
206
|
+
let claudeArgs, child;
|
|
207
|
+
|
|
208
|
+
if (spawnOpts.shell) {
|
|
209
|
+
// When shell is required, escape arguments properly
|
|
210
|
+
claudeArgs = [claudeCli, ...claudeArgsList].map(escapeShellArg).join(' ');
|
|
211
|
+
child = spawn(claudeArgs, spawnOpts);
|
|
212
|
+
} else {
|
|
213
|
+
// When no shell needed, use arguments array directly
|
|
214
|
+
claudeArgs = claudeArgsList;
|
|
215
|
+
child = spawn(claudeCli, claudeArgs, spawnOpts);
|
|
216
|
+
}
|
|
142
217
|
|
|
143
218
|
child.on('exit', (code, signal) => {
|
|
144
219
|
if (signal) {
|
package/bin/claude-detector.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
4
5
|
const { showError, expandPath } = require('./helpers');
|
|
5
6
|
|
|
6
7
|
// Detect Claude CLI executable
|
|
@@ -17,19 +18,62 @@ function detectClaudeCli() {
|
|
|
17
18
|
console.warn(' Falling back to system PATH lookup...');
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
// Priority 2:
|
|
21
|
-
// This
|
|
22
|
-
|
|
21
|
+
// Priority 2: Resolve 'claude' from PATH using which/where.exe
|
|
22
|
+
// This fixes Windows npm installation where spawn() can't resolve bare command names
|
|
23
|
+
// SECURITY: Commands are hardcoded literals with no user input - safe from injection
|
|
24
|
+
const isWindows = process.platform === 'win32';
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const cmd = isWindows ? 'where.exe claude' : 'which claude';
|
|
28
|
+
const result = execSync(cmd, {
|
|
29
|
+
encoding: 'utf8',
|
|
30
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
31
|
+
timeout: 5000 // 5 second timeout to prevent hangs
|
|
32
|
+
}).trim();
|
|
33
|
+
|
|
34
|
+
// where.exe may return multiple lines (all matches in PATH order)
|
|
35
|
+
const matches = result.split('\n').map(p => p.trim()).filter(p => p);
|
|
36
|
+
|
|
37
|
+
if (isWindows) {
|
|
38
|
+
// On Windows, prefer executables with extensions (.exe, .cmd, .bat)
|
|
39
|
+
// where.exe often returns file without extension first, then the actual .cmd wrapper
|
|
40
|
+
const withExtension = matches.find(p => /\.(exe|cmd|bat|ps1)$/i.test(p));
|
|
41
|
+
const claudePath = withExtension || matches[0];
|
|
42
|
+
|
|
43
|
+
if (claudePath && fs.existsSync(claudePath)) {
|
|
44
|
+
return claudePath;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// On Unix, first match is fine
|
|
48
|
+
const claudePath = matches[0];
|
|
49
|
+
|
|
50
|
+
if (claudePath && fs.existsSync(claudePath)) {
|
|
51
|
+
return claudePath;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
// Command failed - claude not in PATH
|
|
56
|
+
// Fall through to return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Priority 3: Claude not found
|
|
60
|
+
return null;
|
|
23
61
|
}
|
|
24
62
|
|
|
25
|
-
// Show Claude not found error
|
|
63
|
+
// Show Claude not found error with diagnostics
|
|
26
64
|
function showClaudeNotFoundError() {
|
|
27
65
|
const isWindows = process.platform === 'win32';
|
|
66
|
+
const pathDirs = (process.env.PATH || '').split(isWindows ? ';' : ':');
|
|
28
67
|
|
|
29
68
|
const errorMsg = `Claude CLI not found in PATH
|
|
30
69
|
|
|
31
70
|
CCS requires Claude CLI to be installed and available in your PATH.
|
|
32
71
|
|
|
72
|
+
[i] Diagnostic Info:
|
|
73
|
+
Platform: ${process.platform}
|
|
74
|
+
PATH directories: ${pathDirs.length}
|
|
75
|
+
Looking for: claude${isWindows ? '.exe' : ''}
|
|
76
|
+
|
|
33
77
|
Solutions:
|
|
34
78
|
1. Install Claude CLI:
|
|
35
79
|
https://docs.claude.com/en/docs/claude-code/installation
|
package/lib/ccs
CHANGED
package/lib/ccs.ps1
CHANGED
|
@@ -72,7 +72,7 @@ Restart your terminal after installation.
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
# Version (updated by scripts/bump-version.sh)
|
|
75
|
-
$CcsVersion = "2.4.
|
|
75
|
+
$CcsVersion = "2.4.3"
|
|
76
76
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
77
77
|
|
|
78
78
|
# Installation function for commands and skills
|