@kaitranntt/ccs 2.5.1 → 3.0.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.
- package/README.ja.md +325 -0
- package/README.md +149 -102
- package/README.vi.md +147 -94
- package/VERSION +1 -1
- package/bin/auth-commands.js +405 -0
- package/bin/ccs.js +113 -35
- package/bin/config-manager.js +36 -7
- package/bin/doctor.js +365 -0
- package/bin/error-manager.js +159 -0
- package/bin/instance-manager.js +218 -0
- package/bin/profile-detector.js +199 -0
- package/bin/profile-registry.js +226 -0
- package/bin/recovery-manager.js +135 -0
- package/lib/ccs +856 -301
- package/lib/ccs.ps1 +792 -122
- package/package.json +1 -1
- package/scripts/postinstall.js +111 -12
package/bin/config-manager.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const { error, expandPath } = require('./helpers');
|
|
7
|
+
const { ErrorManager } = require('./error-manager');
|
|
7
8
|
|
|
8
9
|
// Get config file path
|
|
9
10
|
function getConfigPath() {
|
|
@@ -16,7 +17,15 @@ function readConfig() {
|
|
|
16
17
|
|
|
17
18
|
// Check config exists
|
|
18
19
|
if (!fs.existsSync(configPath)) {
|
|
19
|
-
|
|
20
|
+
// Attempt recovery
|
|
21
|
+
const RecoveryManager = require('./recovery-manager');
|
|
22
|
+
const recovery = new RecoveryManager();
|
|
23
|
+
recovery.ensureConfigJson();
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(configPath)) {
|
|
26
|
+
ErrorManager.showInvalidConfig(configPath, 'File not found');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
// Read and parse JSON
|
|
@@ -25,12 +34,14 @@ function readConfig() {
|
|
|
25
34
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
26
35
|
config = JSON.parse(configContent);
|
|
27
36
|
} catch (e) {
|
|
28
|
-
|
|
37
|
+
ErrorManager.showInvalidConfig(configPath, `Invalid JSON: ${e.message}`);
|
|
38
|
+
process.exit(1);
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
// Validate config has profiles object
|
|
32
42
|
if (!config.profiles || typeof config.profiles !== 'object') {
|
|
33
|
-
|
|
43
|
+
ErrorManager.showInvalidConfig(configPath, "Missing 'profiles' object");
|
|
44
|
+
process.exit(1);
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
return config;
|
|
@@ -44,8 +55,10 @@ function getSettingsPath(profile) {
|
|
|
44
55
|
const settingsPath = config.profiles[profile];
|
|
45
56
|
|
|
46
57
|
if (!settingsPath) {
|
|
47
|
-
const availableProfiles = Object.keys(config.profiles)
|
|
48
|
-
|
|
58
|
+
const availableProfiles = Object.keys(config.profiles);
|
|
59
|
+
const profileList = availableProfiles.map(p => ` - ${p}`);
|
|
60
|
+
ErrorManager.showProfileNotFound(profile, profileList);
|
|
61
|
+
process.exit(1);
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
// Expand path
|
|
@@ -53,7 +66,22 @@ function getSettingsPath(profile) {
|
|
|
53
66
|
|
|
54
67
|
// Validate settings file exists
|
|
55
68
|
if (!fs.existsSync(expandedPath)) {
|
|
56
|
-
|
|
69
|
+
// Auto-create if it's ~/.claude/settings.json
|
|
70
|
+
if (expandedPath.includes('.claude') && expandedPath.endsWith('settings.json')) {
|
|
71
|
+
const RecoveryManager = require('./recovery-manager');
|
|
72
|
+
const recovery = new RecoveryManager();
|
|
73
|
+
recovery.ensureClaudeSettings();
|
|
74
|
+
|
|
75
|
+
if (!fs.existsSync(expandedPath)) {
|
|
76
|
+
ErrorManager.showSettingsNotFound(expandedPath);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log('[i] Auto-created missing settings file');
|
|
81
|
+
} else {
|
|
82
|
+
ErrorManager.showSettingsNotFound(expandedPath);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
57
85
|
}
|
|
58
86
|
|
|
59
87
|
// Validate settings file is valid JSON
|
|
@@ -61,7 +89,8 @@ function getSettingsPath(profile) {
|
|
|
61
89
|
const settingsContent = fs.readFileSync(expandedPath, 'utf8');
|
|
62
90
|
JSON.parse(settingsContent);
|
|
63
91
|
} catch (e) {
|
|
64
|
-
|
|
92
|
+
ErrorManager.showInvalidConfig(expandedPath, `Invalid JSON: ${e.message}`);
|
|
93
|
+
process.exit(1);
|
|
65
94
|
}
|
|
66
95
|
|
|
67
96
|
return expandedPath;
|
package/bin/doctor.js
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
|
+
const { colored } = require('./helpers');
|
|
8
|
+
const { detectClaudeCli } = require('./claude-detector');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Health check results
|
|
12
|
+
*/
|
|
13
|
+
class HealthCheck {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.checks = [];
|
|
16
|
+
this.warnings = [];
|
|
17
|
+
this.errors = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
addCheck(name, status, message = '', fix = null) {
|
|
21
|
+
this.checks.push({ name, status, message, fix });
|
|
22
|
+
|
|
23
|
+
if (status === 'error') this.errors.push({ name, message, fix });
|
|
24
|
+
if (status === 'warning') this.warnings.push({ name, message, fix });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
hasErrors() {
|
|
28
|
+
return this.errors.length > 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
hasWarnings() {
|
|
32
|
+
return this.warnings.length > 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isHealthy() {
|
|
36
|
+
return !this.hasErrors();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* CCS Health Check and Diagnostics
|
|
42
|
+
*/
|
|
43
|
+
class Doctor {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.homedir = os.homedir();
|
|
46
|
+
this.ccsDir = path.join(this.homedir, '.ccs');
|
|
47
|
+
this.claudeDir = path.join(this.homedir, '.claude');
|
|
48
|
+
this.results = new HealthCheck();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Run all health checks
|
|
53
|
+
*/
|
|
54
|
+
async runAllChecks() {
|
|
55
|
+
console.log(colored('Running CCS Health Check...', 'cyan'));
|
|
56
|
+
console.log('');
|
|
57
|
+
|
|
58
|
+
await this.checkClaudeCli();
|
|
59
|
+
this.checkCcsDirectory();
|
|
60
|
+
this.checkConfigFiles();
|
|
61
|
+
this.checkClaudeSettings();
|
|
62
|
+
this.checkProfiles();
|
|
63
|
+
this.checkInstances();
|
|
64
|
+
this.checkPermissions();
|
|
65
|
+
|
|
66
|
+
this.showReport();
|
|
67
|
+
return this.results;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check 1: Claude CLI availability
|
|
72
|
+
*/
|
|
73
|
+
async checkClaudeCli() {
|
|
74
|
+
process.stdout.write('[?] Checking Claude CLI... ');
|
|
75
|
+
|
|
76
|
+
const claudeCli = detectClaudeCli();
|
|
77
|
+
|
|
78
|
+
// Try to execute claude --version
|
|
79
|
+
try {
|
|
80
|
+
const result = await new Promise((resolve, reject) => {
|
|
81
|
+
const child = spawn(claudeCli, ['--version'], {
|
|
82
|
+
stdio: 'pipe',
|
|
83
|
+
timeout: 5000
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let output = '';
|
|
87
|
+
child.stdout.on('data', data => output += data);
|
|
88
|
+
child.stderr.on('data', data => output += data);
|
|
89
|
+
|
|
90
|
+
child.on('close', code => {
|
|
91
|
+
if (code === 0) resolve(output);
|
|
92
|
+
else reject(new Error('Exit code ' + code));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
child.on('error', reject);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
console.log(colored('[OK]', 'green'));
|
|
99
|
+
this.results.addCheck('Claude CLI', 'success', `Found: ${claudeCli}`);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
console.log(colored('[X]', 'red'));
|
|
102
|
+
this.results.addCheck(
|
|
103
|
+
'Claude CLI',
|
|
104
|
+
'error',
|
|
105
|
+
'Claude CLI not found or not working',
|
|
106
|
+
'Install from: https://docs.claude.com/en/docs/claude-code/installation'
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check 2: ~/.ccs/ directory
|
|
113
|
+
*/
|
|
114
|
+
checkCcsDirectory() {
|
|
115
|
+
process.stdout.write('[?] Checking ~/.ccs/ directory... ');
|
|
116
|
+
|
|
117
|
+
if (fs.existsSync(this.ccsDir)) {
|
|
118
|
+
console.log(colored('[OK]', 'green'));
|
|
119
|
+
this.results.addCheck('CCS Directory', 'success');
|
|
120
|
+
} else {
|
|
121
|
+
console.log(colored('[X]', 'red'));
|
|
122
|
+
this.results.addCheck(
|
|
123
|
+
'CCS Directory',
|
|
124
|
+
'error',
|
|
125
|
+
'~/.ccs/ directory not found',
|
|
126
|
+
'Run: npm install -g @kaitranntt/ccs --force'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check 3: Config files
|
|
133
|
+
*/
|
|
134
|
+
checkConfigFiles() {
|
|
135
|
+
const files = [
|
|
136
|
+
{ path: path.join(this.ccsDir, 'config.json'), name: 'config.json' },
|
|
137
|
+
{ path: path.join(this.ccsDir, 'glm.settings.json'), name: 'glm.settings.json' },
|
|
138
|
+
{ path: path.join(this.ccsDir, 'kimi.settings.json'), name: 'kimi.settings.json' }
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
for (const file of files) {
|
|
142
|
+
process.stdout.write(`[?] Checking ${file.name}... `);
|
|
143
|
+
|
|
144
|
+
if (!fs.existsSync(file.path)) {
|
|
145
|
+
console.log(colored('[X]', 'red'));
|
|
146
|
+
this.results.addCheck(
|
|
147
|
+
file.name,
|
|
148
|
+
'error',
|
|
149
|
+
`${file.name} not found`,
|
|
150
|
+
'Run: npm install -g @kaitranntt/ccs --force'
|
|
151
|
+
);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Validate JSON
|
|
156
|
+
try {
|
|
157
|
+
const content = fs.readFileSync(file.path, 'utf8');
|
|
158
|
+
JSON.parse(content);
|
|
159
|
+
console.log(colored('[OK]', 'green'));
|
|
160
|
+
this.results.addCheck(file.name, 'success');
|
|
161
|
+
} catch (e) {
|
|
162
|
+
console.log(colored('[X]', 'red'));
|
|
163
|
+
this.results.addCheck(
|
|
164
|
+
file.name,
|
|
165
|
+
'error',
|
|
166
|
+
`Invalid JSON: ${e.message}`,
|
|
167
|
+
`Backup and recreate: mv ${file.path} ${file.path}.backup && npm install -g @kaitranntt/ccs --force`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check 4: Claude settings
|
|
175
|
+
*/
|
|
176
|
+
checkClaudeSettings() {
|
|
177
|
+
process.stdout.write('[?] Checking ~/.claude/settings.json... ');
|
|
178
|
+
|
|
179
|
+
const settingsPath = path.join(this.claudeDir, 'settings.json');
|
|
180
|
+
|
|
181
|
+
if (!fs.existsSync(settingsPath)) {
|
|
182
|
+
console.log(colored('[!]', 'yellow'));
|
|
183
|
+
this.results.addCheck(
|
|
184
|
+
'Claude Settings',
|
|
185
|
+
'warning',
|
|
186
|
+
'~/.claude/settings.json not found',
|
|
187
|
+
'Run: claude /login'
|
|
188
|
+
);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Validate JSON
|
|
193
|
+
try {
|
|
194
|
+
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
195
|
+
JSON.parse(content);
|
|
196
|
+
console.log(colored('[OK]', 'green'));
|
|
197
|
+
this.results.addCheck('Claude Settings', 'success');
|
|
198
|
+
} catch (e) {
|
|
199
|
+
console.log(colored('[!]', 'yellow'));
|
|
200
|
+
this.results.addCheck(
|
|
201
|
+
'Claude Settings',
|
|
202
|
+
'warning',
|
|
203
|
+
`Invalid JSON: ${e.message}`,
|
|
204
|
+
'Run: claude /login'
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check 5: Profile configurations
|
|
211
|
+
*/
|
|
212
|
+
checkProfiles() {
|
|
213
|
+
process.stdout.write('[?] Checking profiles... ');
|
|
214
|
+
|
|
215
|
+
const configPath = path.join(this.ccsDir, 'config.json');
|
|
216
|
+
if (!fs.existsSync(configPath)) {
|
|
217
|
+
console.log(colored('[SKIP]', 'yellow'));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
223
|
+
|
|
224
|
+
if (!config.profiles || typeof config.profiles !== 'object') {
|
|
225
|
+
console.log(colored('[X]', 'red'));
|
|
226
|
+
this.results.addCheck(
|
|
227
|
+
'Profiles',
|
|
228
|
+
'error',
|
|
229
|
+
'config.json missing profiles object',
|
|
230
|
+
'Run: npm install -g @kaitranntt/ccs --force'
|
|
231
|
+
);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const profileCount = Object.keys(config.profiles).length;
|
|
236
|
+
console.log(colored('[OK]', 'green'), `(${profileCount} profiles)`);
|
|
237
|
+
this.results.addCheck('Profiles', 'success', `${profileCount} profiles configured`);
|
|
238
|
+
} catch (e) {
|
|
239
|
+
console.log(colored('[X]', 'red'));
|
|
240
|
+
this.results.addCheck('Profiles', 'error', e.message);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Check 6: Instance directories (account-based profiles)
|
|
246
|
+
*/
|
|
247
|
+
checkInstances() {
|
|
248
|
+
process.stdout.write('[?] Checking instances... ');
|
|
249
|
+
|
|
250
|
+
const instancesDir = path.join(this.ccsDir, 'instances');
|
|
251
|
+
if (!fs.existsSync(instancesDir)) {
|
|
252
|
+
console.log(colored('[i]', 'cyan'), '(no account profiles)');
|
|
253
|
+
this.results.addCheck('Instances', 'success', 'No account profiles configured');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const instances = fs.readdirSync(instancesDir).filter(name => {
|
|
258
|
+
return fs.statSync(path.join(instancesDir, name)).isDirectory();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
if (instances.length === 0) {
|
|
262
|
+
console.log(colored('[i]', 'cyan'), '(no account profiles)');
|
|
263
|
+
this.results.addCheck('Instances', 'success', 'No account profiles');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(colored('[OK]', 'green'), `(${instances.length} instances)`);
|
|
268
|
+
this.results.addCheck('Instances', 'success', `${instances.length} account profiles`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Check 7: File permissions
|
|
273
|
+
*/
|
|
274
|
+
checkPermissions() {
|
|
275
|
+
process.stdout.write('[?] Checking permissions... ');
|
|
276
|
+
|
|
277
|
+
const testFile = path.join(this.ccsDir, '.permission-test');
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
fs.writeFileSync(testFile, 'test', 'utf8');
|
|
281
|
+
fs.unlinkSync(testFile);
|
|
282
|
+
console.log(colored('[OK]', 'green'));
|
|
283
|
+
this.results.addCheck('Permissions', 'success');
|
|
284
|
+
} catch (e) {
|
|
285
|
+
console.log(colored('[X]', 'red'));
|
|
286
|
+
this.results.addCheck(
|
|
287
|
+
'Permissions',
|
|
288
|
+
'error',
|
|
289
|
+
'Cannot write to ~/.ccs/',
|
|
290
|
+
'Fix: sudo chown -R $USER ~/.ccs ~/.claude && chmod 755 ~/.ccs ~/.claude'
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Show health check report
|
|
297
|
+
*/
|
|
298
|
+
showReport() {
|
|
299
|
+
console.log('');
|
|
300
|
+
console.log(colored('═══════════════════════════════════════════', 'cyan'));
|
|
301
|
+
console.log(colored('Health Check Report', 'bold'));
|
|
302
|
+
console.log(colored('═══════════════════════════════════════════', 'cyan'));
|
|
303
|
+
console.log('');
|
|
304
|
+
|
|
305
|
+
if (this.results.isHealthy() && !this.results.hasWarnings()) {
|
|
306
|
+
console.log(colored('✓ All checks passed!', 'green'));
|
|
307
|
+
console.log('');
|
|
308
|
+
console.log('Your CCS installation is healthy.');
|
|
309
|
+
console.log('');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Show errors
|
|
314
|
+
if (this.results.hasErrors()) {
|
|
315
|
+
console.log(colored('Errors:', 'red'));
|
|
316
|
+
this.results.errors.forEach(err => {
|
|
317
|
+
console.log(` [X] ${err.name}: ${err.message}`);
|
|
318
|
+
if (err.fix) {
|
|
319
|
+
console.log(` Fix: ${err.fix}`);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
console.log('');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Show warnings
|
|
326
|
+
if (this.results.hasWarnings()) {
|
|
327
|
+
console.log(colored('Warnings:', 'yellow'));
|
|
328
|
+
this.results.warnings.forEach(warn => {
|
|
329
|
+
console.log(` [!] ${warn.name}: ${warn.message}`);
|
|
330
|
+
if (warn.fix) {
|
|
331
|
+
console.log(` Fix: ${warn.fix}`);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
console.log('');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Summary
|
|
338
|
+
if (this.results.hasErrors()) {
|
|
339
|
+
console.log(colored('Status: Installation has errors', 'red'));
|
|
340
|
+
console.log('Run suggested fixes above to resolve issues.');
|
|
341
|
+
} else {
|
|
342
|
+
console.log(colored('Status: Installation healthy (warnings only)', 'green'));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
console.log('');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Generate JSON report
|
|
350
|
+
*/
|
|
351
|
+
generateJsonReport() {
|
|
352
|
+
return JSON.stringify({
|
|
353
|
+
timestamp: new Date().toISOString(),
|
|
354
|
+
platform: process.platform,
|
|
355
|
+
nodeVersion: process.version,
|
|
356
|
+
ccsVersion: require('../package.json').version,
|
|
357
|
+
checks: this.results.checks,
|
|
358
|
+
errors: this.results.errors,
|
|
359
|
+
warnings: this.results.warnings,
|
|
360
|
+
healthy: this.results.isHealthy()
|
|
361
|
+
}, null, 2);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
module.exports = Doctor;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { colored } = require('./helpers');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Error types with structured messages
|
|
7
|
+
*/
|
|
8
|
+
const ErrorTypes = {
|
|
9
|
+
NO_CLAUDE_CLI: 'NO_CLAUDE_CLI',
|
|
10
|
+
MISSING_SETTINGS: 'MISSING_SETTINGS',
|
|
11
|
+
INVALID_CONFIG: 'INVALID_CONFIG',
|
|
12
|
+
UNKNOWN_PROFILE: 'UNKNOWN_PROFILE',
|
|
13
|
+
PERMISSION_DENIED: 'PERMISSION_DENIED',
|
|
14
|
+
GENERIC: 'GENERIC'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Enhanced error manager with context-aware messages
|
|
19
|
+
*/
|
|
20
|
+
class ErrorManager {
|
|
21
|
+
/**
|
|
22
|
+
* Show Claude CLI not found error
|
|
23
|
+
*/
|
|
24
|
+
static showClaudeNotFound() {
|
|
25
|
+
console.error('');
|
|
26
|
+
console.error(colored('╔══════════════════════════════════════════════════════════╗', 'red'));
|
|
27
|
+
console.error(colored('║ ERROR: Claude CLI not found ║', 'red'));
|
|
28
|
+
console.error(colored('╚══════════════════════════════════════════════════════════╝', 'red'));
|
|
29
|
+
console.error('');
|
|
30
|
+
console.error('CCS requires Claude CLI to be installed.');
|
|
31
|
+
console.error('');
|
|
32
|
+
console.error(colored('Fix:', 'yellow'));
|
|
33
|
+
console.error(' 1. Install Claude CLI:');
|
|
34
|
+
console.error(' https://docs.claude.com/en/docs/claude-code/installation');
|
|
35
|
+
console.error('');
|
|
36
|
+
console.error(' 2. Verify installation:');
|
|
37
|
+
console.error(' command -v claude (Unix)');
|
|
38
|
+
console.error(' Get-Command claude (Windows)');
|
|
39
|
+
console.error('');
|
|
40
|
+
console.error(' 3. Custom path (if installed elsewhere):');
|
|
41
|
+
console.error(' export CCS_CLAUDE_PATH="/path/to/claude"');
|
|
42
|
+
console.error('');
|
|
43
|
+
console.error('Restart terminal after installation.');
|
|
44
|
+
console.error('');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Show settings file not found error
|
|
49
|
+
* @param {string} settingsPath - Path to missing settings file
|
|
50
|
+
*/
|
|
51
|
+
static showSettingsNotFound(settingsPath) {
|
|
52
|
+
const isClaudeSettings = settingsPath.includes('.claude') && settingsPath.endsWith('settings.json');
|
|
53
|
+
|
|
54
|
+
console.error('');
|
|
55
|
+
console.error(colored('╔══════════════════════════════════════════════════════════╗', 'red'));
|
|
56
|
+
console.error(colored('║ ERROR: Settings file not found ║', 'red'));
|
|
57
|
+
console.error(colored('╚══════════════════════════════════════════════════════════╝', 'red'));
|
|
58
|
+
console.error('');
|
|
59
|
+
console.error(`File: ${settingsPath}`);
|
|
60
|
+
console.error('');
|
|
61
|
+
|
|
62
|
+
if (isClaudeSettings) {
|
|
63
|
+
console.error('This file is auto-created when you login to Claude CLI.');
|
|
64
|
+
console.error('');
|
|
65
|
+
console.error(colored('Fix (copy-paste):', 'yellow'));
|
|
66
|
+
console.error(` echo '{}' > ${settingsPath}`);
|
|
67
|
+
console.error(' claude /login');
|
|
68
|
+
console.error('');
|
|
69
|
+
console.error('Why: Newer Claude CLI versions require explicit login.');
|
|
70
|
+
} else {
|
|
71
|
+
console.error(colored('Fix (copy-paste):', 'yellow'));
|
|
72
|
+
console.error(' npm install -g @kaitranntt/ccs --force');
|
|
73
|
+
console.error('');
|
|
74
|
+
console.error('This will recreate missing profile settings.');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.error('');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Show invalid configuration error
|
|
82
|
+
* @param {string} configPath - Path to invalid config
|
|
83
|
+
* @param {string} errorDetail - JSON parse error detail
|
|
84
|
+
*/
|
|
85
|
+
static showInvalidConfig(configPath, errorDetail) {
|
|
86
|
+
console.error('');
|
|
87
|
+
console.error(colored('╔══════════════════════════════════════════════════════════╗', 'red'));
|
|
88
|
+
console.error(colored('║ ERROR: Configuration invalid ║', 'red'));
|
|
89
|
+
console.error(colored('╚══════════════════════════════════════════════════════════╝', 'red'));
|
|
90
|
+
console.error('');
|
|
91
|
+
console.error(`File: ${configPath}`);
|
|
92
|
+
console.error(`Issue: ${errorDetail}`);
|
|
93
|
+
console.error('');
|
|
94
|
+
console.error(colored('Fix (copy-paste):', 'yellow'));
|
|
95
|
+
console.error(' # Backup corrupted file');
|
|
96
|
+
console.error(` mv ${configPath} ${configPath}.backup`);
|
|
97
|
+
console.error('');
|
|
98
|
+
console.error(' # Reinstall CCS');
|
|
99
|
+
console.error(' npm install -g @kaitranntt/ccs --force');
|
|
100
|
+
console.error('');
|
|
101
|
+
console.error('Your profile settings will be preserved.');
|
|
102
|
+
console.error('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Show profile not found error
|
|
107
|
+
* @param {string} profileName - Requested profile name
|
|
108
|
+
* @param {string[]} availableProfiles - List of available profiles
|
|
109
|
+
* @param {string} suggestion - Suggested profile name (fuzzy match)
|
|
110
|
+
*/
|
|
111
|
+
static showProfileNotFound(profileName, availableProfiles, suggestion = null) {
|
|
112
|
+
console.error('');
|
|
113
|
+
console.error(colored('╔══════════════════════════════════════════════════════════╗', 'red'));
|
|
114
|
+
console.error(colored(`║ ERROR: Profile '${profileName}' not found${' '.repeat(Math.max(0, 35 - profileName.length))}║`, 'red'));
|
|
115
|
+
console.error(colored('╚══════════════════════════════════════════════════════════╝', 'red'));
|
|
116
|
+
console.error('');
|
|
117
|
+
console.error(colored('Available profiles:', 'cyan'));
|
|
118
|
+
availableProfiles.forEach(line => console.error(` ${line}`));
|
|
119
|
+
console.error('');
|
|
120
|
+
console.error(colored('Fix:', 'yellow'));
|
|
121
|
+
console.error(' # Use existing profile');
|
|
122
|
+
console.error(' ccs <profile> "your prompt"');
|
|
123
|
+
console.error('');
|
|
124
|
+
console.error(' # Create new account profile');
|
|
125
|
+
console.error(' ccs auth create <name>');
|
|
126
|
+
console.error('');
|
|
127
|
+
|
|
128
|
+
if (suggestion) {
|
|
129
|
+
console.error(colored(`Did you mean: ${suggestion}`, 'yellow'));
|
|
130
|
+
console.error('');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Show permission denied error
|
|
136
|
+
* @param {string} path - Path with permission issue
|
|
137
|
+
*/
|
|
138
|
+
static showPermissionDenied(path) {
|
|
139
|
+
console.error('');
|
|
140
|
+
console.error(colored('╔══════════════════════════════════════════════════════════╗', 'red'));
|
|
141
|
+
console.error(colored('║ ERROR: Permission denied ║', 'red'));
|
|
142
|
+
console.error(colored('╚══════════════════════════════════════════════════════════╝', 'red'));
|
|
143
|
+
console.error('');
|
|
144
|
+
console.error(`Cannot write to: ${path}`);
|
|
145
|
+
console.error('');
|
|
146
|
+
console.error(colored('Fix (copy-paste):', 'yellow'));
|
|
147
|
+
console.error(' # Fix ownership');
|
|
148
|
+
console.error(' sudo chown -R $USER ~/.ccs ~/.claude');
|
|
149
|
+
console.error('');
|
|
150
|
+
console.error(' # Fix permissions');
|
|
151
|
+
console.error(' chmod 755 ~/.ccs ~/.claude');
|
|
152
|
+
console.error('');
|
|
153
|
+
console.error(' # Retry installation');
|
|
154
|
+
console.error(' npm install -g @kaitranntt/ccs --force');
|
|
155
|
+
console.error('');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { ErrorManager, ErrorTypes };
|