@kaitranntt/ccs 2.4.4 → 2.4.5
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 +21 -9
- package/README.vi.md +13 -1
- package/VERSION +1 -1
- package/bin/ccs.js +16 -109
- package/bin/claude-detector.js +5 -34
- package/bin/config-manager.js +8 -69
- package/bin/helpers.js +7 -23
- package/lib/ccs +107 -71
- package/lib/ccs.ps1 +210 -364
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ export CCS_CLAUDE_PATH="/path/to/claude" # Unix
|
|
|
108
108
|
$env:CCS_CLAUDE_PATH = "D:\Tools\Claude\claude.exe" # Windows
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
**See [Troubleshooting Guide](./docs/troubleshooting.md#claude-cli-in-non-standard-location) for detailed setup instructions.**
|
|
111
|
+
**See [Troubleshooting Guide](./docs/en/troubleshooting.md#claude-cli-in-non-standard-location) for detailed setup instructions.**
|
|
112
112
|
|
|
113
113
|
---
|
|
114
114
|
|
|
@@ -235,7 +235,9 @@ ccs --uninstall # Remove CCS commands and skills from ~/.claude/
|
|
|
235
235
|
|
|
236
236
|
---
|
|
237
237
|
|
|
238
|
-
### 🗑️ Uninstall
|
|
238
|
+
### 🗑️ Official Uninstall
|
|
239
|
+
|
|
240
|
+
**The recommended way to completely remove CCS:**
|
|
239
241
|
|
|
240
242
|
**macOS / Linux**:
|
|
241
243
|
```bash
|
|
@@ -247,6 +249,16 @@ curl -fsSL ccs.kaitran.ca/uninstall | bash
|
|
|
247
249
|
irm ccs.kaitran.ca/uninstall | iex
|
|
248
250
|
```
|
|
249
251
|
|
|
252
|
+
> 💡 **Why use the official uninstaller?**
|
|
253
|
+
> - Removes all CCS files and configurations
|
|
254
|
+
> - Cleans up PATH modifications
|
|
255
|
+
> - Removes Claude CLI commands/skills
|
|
256
|
+
> - Handles edge cases we've tested
|
|
257
|
+
|
|
258
|
+
**Alternative methods** (if official uninstaller fails):
|
|
259
|
+
- **npm**: `npm uninstall -g @kaitranntt/ccs`
|
|
260
|
+
- **Manual**: See [troubleshooting guide](./docs/en/troubleshooting.md#manual-uninstall)
|
|
261
|
+
|
|
250
262
|
---
|
|
251
263
|
|
|
252
264
|
## 🎯 Philosophy
|
|
@@ -260,17 +272,17 @@ irm ccs.kaitran.ca/uninstall | iex
|
|
|
260
272
|
## 📖 Documentation
|
|
261
273
|
|
|
262
274
|
**Complete documentation in [docs/](./docs/)**:
|
|
263
|
-
- [Installation Guide](./docs/installation.md)
|
|
264
|
-
- [Configuration](./docs/configuration.md)
|
|
265
|
-
- [Usage Examples](./docs/usage.md)
|
|
266
|
-
- [Troubleshooting](./docs/troubleshooting.md)
|
|
267
|
-
- [Contributing](./docs/contributing.md)
|
|
275
|
+
- [Installation Guide](./docs/en/installation.md)
|
|
276
|
+
- [Configuration](./docs/en/configuration.md)
|
|
277
|
+
- [Usage Examples](./docs/en/usage.md)
|
|
278
|
+
- [Troubleshooting](./docs/en/troubleshooting.md)
|
|
279
|
+
- [Contributing](./docs/en/contributing.md)
|
|
268
280
|
|
|
269
281
|
---
|
|
270
282
|
|
|
271
283
|
## 🤝 Contributing
|
|
272
284
|
|
|
273
|
-
We welcome contributions! Please see our [Contributing Guide](./docs/contributing.md) for details.
|
|
285
|
+
We welcome contributions! Please see our [Contributing Guide](./docs/en/contributing.md) for details.
|
|
274
286
|
|
|
275
287
|
---
|
|
276
288
|
|
|
@@ -284,6 +296,6 @@ CCS is licensed under the [MIT License](LICENSE).
|
|
|
284
296
|
|
|
285
297
|
**Made with ❤️ for developers who hit rate limits too often**
|
|
286
298
|
|
|
287
|
-
[⭐ Star this repo](https://github.com/kaitranntt/ccs) | [🐛 Report issues](https://github.com/kaitranntt/ccs/issues) | [📖 Read docs](./docs/)
|
|
299
|
+
[⭐ Star this repo](https://github.com/kaitranntt/ccs) | [🐛 Report issues](https://github.com/kaitranntt/ccs/issues) | [📖 Read docs](./docs/en/)
|
|
288
300
|
|
|
289
301
|
</div>
|
package/README.vi.md
CHANGED
|
@@ -234,7 +234,9 @@ ccs --uninstall # Gỡ bỏ lệnh và kỹ năng CCS khỏi ~/.claude/
|
|
|
234
234
|
|
|
235
235
|
---
|
|
236
236
|
|
|
237
|
-
### 🗑️ Gỡ Cài Đặt
|
|
237
|
+
### 🗑️ Gỡ Cài Đặt Chính Thức
|
|
238
|
+
|
|
239
|
+
**Cách được khuyến nghị để gỡ bỏ hoàn toàn CCS:**
|
|
238
240
|
|
|
239
241
|
**macOS / Linux**:
|
|
240
242
|
```bash
|
|
@@ -246,6 +248,16 @@ curl -fsSL ccs.kaitran.ca/uninstall | bash
|
|
|
246
248
|
irm ccs.kaitran.ca/uninstall | iex
|
|
247
249
|
```
|
|
248
250
|
|
|
251
|
+
> 💡 **Tại sao dùng uninstaller chính thức?**
|
|
252
|
+
> - Gỡ bỏ tất cả file và cấu hình CCS
|
|
253
|
+
> - Dọn dẹp PATH modifications
|
|
254
|
+
> - Gỡ bỏ commands/skills Claude CLI
|
|
255
|
+
> - Xử lý các trường hợp đặc biệt đã test
|
|
256
|
+
|
|
257
|
+
**Phương pháp thay thế** (nếu uninstaller chính thức thất bại):
|
|
258
|
+
- **npm**: `npm uninstall -g @kaitranntt/ccs`
|
|
259
|
+
- **Thủ công**: Xem [hướng dẫn khắc phục](./docs/vi/troubleshooting.vi.md#gỡ-cài-đặt-thủ-công)
|
|
260
|
+
|
|
249
261
|
---
|
|
250
262
|
|
|
251
263
|
## 🎯 Triết Lý
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.4.
|
|
1
|
+
2.4.5
|
package/bin/ccs.js
CHANGED
|
@@ -4,35 +4,24 @@
|
|
|
4
4
|
const { spawn } = require('child_process');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const fs = require('fs');
|
|
7
|
-
const {
|
|
7
|
+
const { error } = require('./helpers');
|
|
8
8
|
const { detectClaudeCli, showClaudeNotFoundError } = require('./claude-detector');
|
|
9
9
|
const { getSettingsPath } = require('./config-manager');
|
|
10
10
|
|
|
11
11
|
// Version (sync with package.json)
|
|
12
12
|
const CCS_VERSION = require('../package.json').version;
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
}
|
|
14
|
+
// Execute Claude CLI with unified spawn logic
|
|
15
|
+
function execClaude(claudeCli, args) {
|
|
16
|
+
const child = spawn(claudeCli, args, { stdio: 'inherit', windowsHide: true });
|
|
17
|
+
child.on('exit', (code, signal) => {
|
|
18
|
+
if (signal) process.kill(process.pid, signal);
|
|
19
|
+
else process.exit(code || 0);
|
|
20
|
+
});
|
|
21
|
+
child.on('error', () => {
|
|
22
|
+
showClaudeNotFoundError();
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
36
25
|
}
|
|
37
26
|
|
|
38
27
|
// Special command handlers
|
|
@@ -51,39 +40,12 @@ function handleVersionCommand() {
|
|
|
51
40
|
|
|
52
41
|
function handleHelpCommand(remainingArgs) {
|
|
53
42
|
const claudeCli = detectClaudeCli();
|
|
54
|
-
|
|
55
|
-
// Check if claude was found
|
|
56
43
|
if (!claudeCli) {
|
|
57
44
|
showClaudeNotFoundError();
|
|
58
45
|
process.exit(1);
|
|
59
46
|
}
|
|
60
47
|
|
|
61
|
-
|
|
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
|
-
}
|
|
74
|
-
|
|
75
|
-
child.on('exit', (code, signal) => {
|
|
76
|
-
if (signal) {
|
|
77
|
-
process.kill(process.pid, signal);
|
|
78
|
-
} else {
|
|
79
|
-
process.exit(code || 0);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
child.on('error', (err) => {
|
|
84
|
-
showClaudeNotFoundError();
|
|
85
|
-
process.exit(1);
|
|
86
|
-
});
|
|
48
|
+
execClaude(claudeCli, ['--help', ...remainingArgs]);
|
|
87
49
|
}
|
|
88
50
|
|
|
89
51
|
function handleInstallCommand() {
|
|
@@ -91,9 +53,7 @@ function handleInstallCommand() {
|
|
|
91
53
|
console.log('[Installing CCS Commands and Skills]');
|
|
92
54
|
console.log('Feature not yet implemented in Node.js standalone');
|
|
93
55
|
console.log('Use traditional installer for now:');
|
|
94
|
-
console.log(
|
|
95
|
-
? ' irm ccs.kaitran.ca/install | iex'
|
|
96
|
-
: ' curl -fsSL ccs.kaitran.ca/install | bash');
|
|
56
|
+
console.log(' curl -fsSL ccs.kaitran.ca/install | bash');
|
|
97
57
|
process.exit(0);
|
|
98
58
|
}
|
|
99
59
|
|
|
@@ -151,40 +111,12 @@ function main() {
|
|
|
151
111
|
// Special case: "default" profile just runs claude directly
|
|
152
112
|
if (profile === 'default') {
|
|
153
113
|
const claudeCli = detectClaudeCli();
|
|
154
|
-
|
|
155
|
-
// Check if claude was found
|
|
156
114
|
if (!claudeCli) {
|
|
157
115
|
showClaudeNotFoundError();
|
|
158
116
|
process.exit(1);
|
|
159
117
|
}
|
|
160
118
|
|
|
161
|
-
|
|
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
|
-
}
|
|
174
|
-
|
|
175
|
-
child.on('exit', (code, signal) => {
|
|
176
|
-
if (signal) {
|
|
177
|
-
process.kill(process.pid, signal);
|
|
178
|
-
} else {
|
|
179
|
-
process.exit(code || 0);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
child.on('error', (err) => {
|
|
184
|
-
showClaudeNotFoundError();
|
|
185
|
-
process.exit(1);
|
|
186
|
-
});
|
|
187
|
-
|
|
119
|
+
execClaude(claudeCli, remainingArgs);
|
|
188
120
|
return;
|
|
189
121
|
}
|
|
190
122
|
|
|
@@ -201,32 +133,7 @@ function main() {
|
|
|
201
133
|
}
|
|
202
134
|
|
|
203
135
|
// Execute claude with --settings
|
|
204
|
-
|
|
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
|
-
}
|
|
217
|
-
|
|
218
|
-
child.on('exit', (code, signal) => {
|
|
219
|
-
if (signal) {
|
|
220
|
-
process.kill(process.pid, signal);
|
|
221
|
-
} else {
|
|
222
|
-
process.exit(code || 0);
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
child.on('error', (err) => {
|
|
227
|
-
showClaudeNotFoundError();
|
|
228
|
-
process.exit(1);
|
|
229
|
-
});
|
|
136
|
+
execClaude(claudeCli, ['--settings', settingsPath, ...remainingArgs]);
|
|
230
137
|
}
|
|
231
138
|
|
|
232
139
|
// Run main
|
package/bin/claude-detector.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
|
-
const {
|
|
5
|
+
const { expandPath } = require('./helpers');
|
|
6
6
|
|
|
7
7
|
// Detect Claude CLI executable
|
|
8
8
|
function detectClaudeCli() {
|
|
@@ -60,40 +60,11 @@ function detectClaudeCli() {
|
|
|
60
60
|
return null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// Show Claude not found error
|
|
63
|
+
// Show Claude not found error
|
|
64
64
|
function showClaudeNotFoundError() {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const errorMsg = `Claude CLI not found in PATH
|
|
69
|
-
|
|
70
|
-
CCS requires Claude CLI to be installed and available in your PATH.
|
|
71
|
-
|
|
72
|
-
[i] Diagnostic Info:
|
|
73
|
-
Platform: ${process.platform}
|
|
74
|
-
PATH directories: ${pathDirs.length}
|
|
75
|
-
Looking for: claude${isWindows ? '.exe' : ''}
|
|
76
|
-
|
|
77
|
-
Solutions:
|
|
78
|
-
1. Install Claude CLI:
|
|
79
|
-
https://docs.claude.com/en/docs/claude-code/installation
|
|
80
|
-
|
|
81
|
-
2. Verify installation:
|
|
82
|
-
${isWindows ? 'Get-Command claude' : 'command -v claude'}
|
|
83
|
-
|
|
84
|
-
3. If installed but not in PATH, add it:
|
|
85
|
-
# Find Claude installation
|
|
86
|
-
${isWindows ? 'where.exe claude' : 'which claude'}
|
|
87
|
-
|
|
88
|
-
# Or set custom path
|
|
89
|
-
${isWindows
|
|
90
|
-
? '$env:CCS_CLAUDE_PATH = \'C:\\path\\to\\claude.exe\''
|
|
91
|
-
: 'export CCS_CLAUDE_PATH=\'/path/to/claude\''
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
Restart your terminal after installation.`;
|
|
95
|
-
|
|
96
|
-
showError(errorMsg);
|
|
65
|
+
console.error('ERROR: Claude CLI not found in PATH');
|
|
66
|
+
console.error('Install from: https://docs.claude.com/en/docs/claude-code/installation');
|
|
67
|
+
process.exit(1);
|
|
97
68
|
}
|
|
98
69
|
|
|
99
70
|
module.exports = {
|
package/bin/config-manager.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
-
const {
|
|
6
|
+
const { error, expandPath } = require('./helpers');
|
|
7
7
|
|
|
8
8
|
// Get config file path
|
|
9
9
|
function getConfigPath() {
|
|
@@ -16,30 +16,7 @@ function readConfig() {
|
|
|
16
16
|
|
|
17
17
|
// Check config exists
|
|
18
18
|
if (!fs.existsSync(configPath)) {
|
|
19
|
-
|
|
20
|
-
showError(`Config file not found: ${configPath}
|
|
21
|
-
|
|
22
|
-
Solutions:
|
|
23
|
-
1. Reinstall CCS (auto-creates config):
|
|
24
|
-
npm install -g @kaitranntt/ccs --force
|
|
25
|
-
|
|
26
|
-
2. Or use traditional installer:
|
|
27
|
-
${isWindows ? 'irm ccs.kaitran.ca/install | iex' : 'curl -fsSL ccs.kaitran.ca/install | bash'}
|
|
28
|
-
|
|
29
|
-
3. Or create manually:
|
|
30
|
-
mkdir -p ~/.ccs
|
|
31
|
-
cat > ~/.ccs/config.json << 'EOF'
|
|
32
|
-
{
|
|
33
|
-
"profiles": {
|
|
34
|
-
"glm": "~/.ccs/glm.settings.json",
|
|
35
|
-
"default": "~/.claude/settings.json"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
EOF
|
|
39
|
-
|
|
40
|
-
Note: If you installed with npm --ignore-scripts, configs weren't created.
|
|
41
|
-
Reinstall without that flag: npm install -g @kaitranntt/ccs --force`);
|
|
42
|
-
process.exit(1);
|
|
19
|
+
error(`Config file not found: ${configPath}`);
|
|
43
20
|
}
|
|
44
21
|
|
|
45
22
|
// Read and parse JSON
|
|
@@ -48,23 +25,12 @@ EOF
|
|
|
48
25
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
49
26
|
config = JSON.parse(configContent);
|
|
50
27
|
} catch (e) {
|
|
51
|
-
|
|
52
|
-
showError(`Invalid JSON in ${configPath}
|
|
53
|
-
|
|
54
|
-
Fix the JSON syntax or reinstall:
|
|
55
|
-
${isWindows ? 'irm ccs.kaitran.ca/install | iex' : 'curl -fsSL ccs.kaitran.ca/install | bash'}`);
|
|
56
|
-
process.exit(1);
|
|
28
|
+
error(`Invalid JSON in ${configPath}: ${e.message}`);
|
|
57
29
|
}
|
|
58
30
|
|
|
59
31
|
// Validate config has profiles object
|
|
60
32
|
if (!config.profiles || typeof config.profiles !== 'object') {
|
|
61
|
-
|
|
62
|
-
showError(`Config must have 'profiles' object
|
|
63
|
-
|
|
64
|
-
See config.example.json for correct format
|
|
65
|
-
Or reinstall:
|
|
66
|
-
${isWindows ? 'irm ccs.kaitran.ca/install | iex' : 'curl -fsSL ccs.kaitran.ca/install | bash'}`);
|
|
67
|
-
process.exit(1);
|
|
33
|
+
error(`Config must have 'profiles' object in ${configPath}`);
|
|
68
34
|
}
|
|
69
35
|
|
|
70
36
|
return config;
|
|
@@ -74,24 +40,12 @@ Or reinstall:
|
|
|
74
40
|
function getSettingsPath(profile) {
|
|
75
41
|
const config = readConfig();
|
|
76
42
|
|
|
77
|
-
// Validate profile name
|
|
78
|
-
if (!validateProfileName(profile)) {
|
|
79
|
-
showError(`Invalid profile name: ${profile}
|
|
80
|
-
|
|
81
|
-
Use only alphanumeric characters, dash, or underscore.`);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
43
|
// Get settings path
|
|
86
44
|
const settingsPath = config.profiles[profile];
|
|
87
45
|
|
|
88
46
|
if (!settingsPath) {
|
|
89
|
-
const availableProfiles = Object.keys(config.profiles).
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Available profiles:
|
|
93
|
-
${availableProfiles}`);
|
|
94
|
-
process.exit(1);
|
|
47
|
+
const availableProfiles = Object.keys(config.profiles).join(', ');
|
|
48
|
+
error(`Profile '${profile}' not found. Available: ${availableProfiles}`);
|
|
95
49
|
}
|
|
96
50
|
|
|
97
51
|
// Expand path
|
|
@@ -99,14 +53,7 @@ ${availableProfiles}`);
|
|
|
99
53
|
|
|
100
54
|
// Validate settings file exists
|
|
101
55
|
if (!fs.existsSync(expandedPath)) {
|
|
102
|
-
|
|
103
|
-
showError(`Settings file not found: ${expandedPath}
|
|
104
|
-
|
|
105
|
-
Solutions:
|
|
106
|
-
1. Create the settings file for profile '${profile}'
|
|
107
|
-
2. Update the path in ${getConfigPath()}
|
|
108
|
-
3. Or reinstall: ${isWindows ? 'irm ccs.kaitran.ca/install | iex' : 'curl -fsSL ccs.kaitran.ca/install | bash'}`);
|
|
109
|
-
process.exit(1);
|
|
56
|
+
error(`Settings file not found: ${expandedPath}`);
|
|
110
57
|
}
|
|
111
58
|
|
|
112
59
|
// Validate settings file is valid JSON
|
|
@@ -114,15 +61,7 @@ Solutions:
|
|
|
114
61
|
const settingsContent = fs.readFileSync(expandedPath, 'utf8');
|
|
115
62
|
JSON.parse(settingsContent);
|
|
116
63
|
} catch (e) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
Details: ${e.message}
|
|
120
|
-
|
|
121
|
-
Solutions:
|
|
122
|
-
1. Validate JSON at https://jsonlint.com
|
|
123
|
-
2. Or reset to template: echo '{"env":{}}' > ${expandedPath}
|
|
124
|
-
3. Or reinstall CCS`);
|
|
125
|
-
process.exit(1);
|
|
64
|
+
error(`Invalid JSON in ${expandedPath}: ${e.message}`);
|
|
126
65
|
}
|
|
127
66
|
|
|
128
67
|
return expandedPath;
|
package/bin/helpers.js
CHANGED
|
@@ -15,15 +15,11 @@ const colors = useColors ? {
|
|
|
15
15
|
reset: '\x1b[0m'
|
|
16
16
|
} : { red: '', yellow: '', cyan: '', green: '', bold: '', reset: '' };
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
function
|
|
20
|
-
console.error(
|
|
21
|
-
console.error(
|
|
22
|
-
|
|
23
|
-
console.error(colors.red + colors.bold + '╚═════════════════════════════════════════════╝' + colors.reset);
|
|
24
|
-
console.error('');
|
|
25
|
-
console.error(colors.red + message + colors.reset);
|
|
26
|
-
console.error('');
|
|
18
|
+
// Simple error formatting
|
|
19
|
+
function error(message) {
|
|
20
|
+
console.error(`ERROR: ${message}`);
|
|
21
|
+
console.error('Try: npm install -g @kaitranntt/ccs --force');
|
|
22
|
+
process.exit(1);
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
// Path expansion (~ and env vars)
|
|
@@ -45,21 +41,9 @@ function expandPath(pathStr) {
|
|
|
45
41
|
return path.normalize(pathStr);
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
// Validate profile name (alphanumeric, dash, underscore only)
|
|
49
|
-
function validateProfileName(profile) {
|
|
50
|
-
return /^[a-zA-Z0-9_-]+$/.test(profile);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Validate path safety (prevent injection)
|
|
54
|
-
function isPathSafe(pathStr) {
|
|
55
|
-
// Allow: alphanumeric, path separators, space, dash, underscore, dot, colon, tilde
|
|
56
|
-
return !/[;|&<>`$*?\[\]'"()]/.test(pathStr);
|
|
57
|
-
}
|
|
58
44
|
|
|
59
45
|
module.exports = {
|
|
60
46
|
colors,
|
|
61
|
-
|
|
62
|
-
expandPath
|
|
63
|
-
validateProfileName,
|
|
64
|
-
isPathSafe
|
|
47
|
+
error,
|
|
48
|
+
expandPath
|
|
65
49
|
};
|