mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 06:16:37 +00:00
feat: improve UX for ccs doctor and sync commands (#13)
* feat: enhance UX for 'ccs sync' and 'ccs doctor' commands - Add ora spinners for real-time progress feedback - Add cli-table3 for structured health check summary table - Display detailed component information in doctor report - Show file/directory counts during sync operations - Improve visual hierarchy and readability - Fix Unicode checkmark violation (replaced with [OK]) Breaking changes: None Dependencies added: ora@^5.4.1, cli-table3@^0.6.5 Version bumped to 4.3.0 * fix: match doctor summary header width to table width * feat: improve settings validation to check API keys, not just endpoints - Check if API keys are configured or still placeholders - Show 'Key configured' vs 'Placeholder key (not configured)' - Previous message 'Valid (API: api.z.ai)' was misleading - Now users can clearly see which profiles need API key configuration - Warnings shown for placeholder keys (yellow [!]) * feat: add category grouping and alignment to doctor checks - Group checks into logical categories: System, Configuration, Profiles & Delegation, System Health - Add consistent padding (26 chars) for aligned columns - Add 2-space indent for better visual hierarchy - Separate categories with blank lines for better readability - Much cleaner and easier to scan than before * fix: perfect alignment for all doctor check output - Use .padEnd(26) consistently for all component names - Remove extra space before status icons - Now all status icons [OK]/[X]/[!]/[i] align perfectly - All component names pad to exactly 26 characters
This commit is contained in:
committed by
kaitranntt
parent
d34dbcac1f
commit
67dbd3f259
+12
-2
@@ -271,17 +271,27 @@ async function handleDoctorCommand() {
|
||||
}
|
||||
|
||||
async function handleSyncCommand() {
|
||||
const { colored } = require('./utils/helpers');
|
||||
|
||||
console.log('');
|
||||
console.log(colored('Syncing CCS Components...', 'cyan'));
|
||||
console.log('');
|
||||
|
||||
// First, copy .claude/ directory from package to ~/.ccs/.claude/
|
||||
const ClaudeDirInstaller = require('./utils/claude-dir-installer');
|
||||
const installer = new ClaudeDirInstaller();
|
||||
installer.install();
|
||||
|
||||
console.log('');
|
||||
|
||||
// Then, create symlinks from ~/.ccs/.claude/ to ~/.claude/
|
||||
const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
|
||||
const manager = new ClaudeSymlinkManager();
|
||||
manager.install(false);
|
||||
|
||||
console.log('[i] Syncing delegation commands and skills to ~/.claude/...');
|
||||
manager.sync();
|
||||
console.log('');
|
||||
console.log(colored('[OK] Sync complete!', 'green'));
|
||||
console.log('');
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
+202
-77
@@ -6,6 +6,8 @@ const os = require('os');
|
||||
const { spawn } = require('child_process');
|
||||
const { colored } = require('../utils/helpers');
|
||||
const { detectClaudeCli } = require('../utils/claude-detector');
|
||||
const ora = require('ora');
|
||||
const Table = require('cli-table3');
|
||||
|
||||
/**
|
||||
* Health check results
|
||||
@@ -15,13 +17,19 @@ class HealthCheck {
|
||||
this.checks = [];
|
||||
this.warnings = [];
|
||||
this.errors = [];
|
||||
this.details = {}; // Store detailed information for summary table
|
||||
}
|
||||
|
||||
addCheck(name, status, message = '', fix = null) {
|
||||
addCheck(name, status, message = '', fix = null, details = null) {
|
||||
this.checks.push({ name, status, message, fix });
|
||||
|
||||
if (status === 'error') this.errors.push({ name, message, fix });
|
||||
if (status === 'warning') this.warnings.push({ name, message, fix });
|
||||
|
||||
// Store details for summary table
|
||||
if (details) {
|
||||
this.details[name] = details;
|
||||
}
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
@@ -46,6 +54,13 @@ class Doctor {
|
||||
this.ccsDir = path.join(this.homedir, '.ccs');
|
||||
this.claudeDir = path.join(this.homedir, '.claude');
|
||||
this.results = new HealthCheck();
|
||||
|
||||
// Get CCS version
|
||||
try {
|
||||
this.ccsVersion = require('../../package.json').version;
|
||||
} catch (e) {
|
||||
this.ccsVersion = 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,15 +70,33 @@ class Doctor {
|
||||
console.log(colored('Running CCS Health Check...', 'cyan'));
|
||||
console.log('');
|
||||
|
||||
// Store CCS version in details
|
||||
this.results.details['CCS Version'] = { status: 'OK', info: `v${this.ccsVersion}` };
|
||||
|
||||
// Group 1: System
|
||||
console.log(colored('System:', 'bold'));
|
||||
await this.checkClaudeCli();
|
||||
this.checkCcsDirectory();
|
||||
console.log('');
|
||||
|
||||
// Group 2: Configuration
|
||||
console.log(colored('Configuration:', 'bold'));
|
||||
this.checkConfigFiles();
|
||||
this.checkClaudeSettings();
|
||||
console.log('');
|
||||
|
||||
// Group 3: Profiles & Delegation
|
||||
console.log(colored('Profiles & Delegation:', 'bold'));
|
||||
this.checkProfiles();
|
||||
this.checkInstances();
|
||||
this.checkDelegation();
|
||||
console.log('');
|
||||
|
||||
// Group 4: System Health
|
||||
console.log(colored('System Health:', 'bold'));
|
||||
this.checkPermissions();
|
||||
this.checkCcsSymlinks();
|
||||
console.log('');
|
||||
|
||||
this.showReport();
|
||||
return this.results;
|
||||
@@ -73,7 +106,7 @@ class Doctor {
|
||||
* Check 1: Claude CLI availability
|
||||
*/
|
||||
async checkClaudeCli() {
|
||||
process.stdout.write('[?] Checking Claude CLI... ');
|
||||
const spinner = ora('Checking Claude CLI').start();
|
||||
|
||||
const claudeCli = detectClaudeCli();
|
||||
|
||||
@@ -97,15 +130,23 @@ class Doctor {
|
||||
child.on('error', reject);
|
||||
});
|
||||
|
||||
console.log(colored('[OK]', 'green'));
|
||||
this.results.addCheck('Claude CLI', 'success', `Found: ${claudeCli}`);
|
||||
// Extract version from output
|
||||
const versionMatch = result.match(/(\d+\.\d+\.\d+)/);
|
||||
const version = versionMatch ? versionMatch[1] : 'unknown';
|
||||
|
||||
spinner.succeed(` ${'Claude CLI'.padEnd(26)}${colored('[OK]', 'green')} ${claudeCli} (v${version})`);
|
||||
this.results.addCheck('Claude CLI', 'success', `Found: ${claudeCli}`, null, {
|
||||
status: 'OK',
|
||||
info: `v${version} (${claudeCli})`
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(colored('[X]', 'red'));
|
||||
spinner.fail(` ${'Claude CLI'.padEnd(26)}${colored('[X]', 'red')} Not found or not working`);
|
||||
this.results.addCheck(
|
||||
'Claude CLI',
|
||||
'error',
|
||||
'Claude CLI not found or not working',
|
||||
'Install from: https://docs.claude.com/en/docs/claude-code/installation'
|
||||
'Install from: https://docs.claude.com/en/docs/claude-code/installation',
|
||||
{ status: 'ERROR', info: 'Not installed' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -114,18 +155,22 @@ class Doctor {
|
||||
* Check 2: ~/.ccs/ directory
|
||||
*/
|
||||
checkCcsDirectory() {
|
||||
process.stdout.write('[?] Checking ~/.ccs/ directory... ');
|
||||
const spinner = ora('Checking ~/.ccs/ directory').start();
|
||||
|
||||
if (fs.existsSync(this.ccsDir)) {
|
||||
console.log(colored('[OK]', 'green'));
|
||||
this.results.addCheck('CCS Directory', 'success');
|
||||
spinner.succeed(` ${'CCS Directory'.padEnd(26)}${colored('[OK]', 'green')} ~/.ccs/`);
|
||||
this.results.addCheck('CCS Directory', 'success', null, null, {
|
||||
status: 'OK',
|
||||
info: '~/.ccs/'
|
||||
});
|
||||
} else {
|
||||
console.log(colored('[X]', 'red'));
|
||||
spinner.fail(` ${'CCS Directory'.padEnd(26)}${colored('[X]', 'red')} Not found`);
|
||||
this.results.addCheck(
|
||||
'CCS Directory',
|
||||
'error',
|
||||
'~/.ccs/ directory not found',
|
||||
'Run: npm install -g @kaitranntt/ccs --force'
|
||||
'Run: npm install -g @kaitranntt/ccs --force',
|
||||
{ status: 'ERROR', info: 'Not found' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -135,21 +180,24 @@ class Doctor {
|
||||
*/
|
||||
checkConfigFiles() {
|
||||
const files = [
|
||||
{ path: path.join(this.ccsDir, 'config.json'), name: 'config.json' },
|
||||
{ path: path.join(this.ccsDir, 'glm.settings.json'), name: 'glm.settings.json' },
|
||||
{ path: path.join(this.ccsDir, 'kimi.settings.json'), name: 'kimi.settings.json' }
|
||||
{ path: path.join(this.ccsDir, 'config.json'), name: 'config.json', key: 'config.json' },
|
||||
{ path: path.join(this.ccsDir, 'glm.settings.json'), name: 'glm.settings.json', key: 'GLM Settings', profile: 'glm' },
|
||||
{ path: path.join(this.ccsDir, 'kimi.settings.json'), name: 'kimi.settings.json', key: 'Kimi Settings', profile: 'kimi' }
|
||||
];
|
||||
|
||||
const { DelegationValidator } = require('../utils/delegation-validator');
|
||||
|
||||
for (const file of files) {
|
||||
process.stdout.write(`[?] Checking ${file.name}... `);
|
||||
const spinner = ora(`Checking ${file.name}`).start();
|
||||
|
||||
if (!fs.existsSync(file.path)) {
|
||||
console.log(colored('[X]', 'red'));
|
||||
spinner.fail(` ${file.name.padEnd(26)}${colored('[X]', 'red')} Not found`);
|
||||
this.results.addCheck(
|
||||
file.name,
|
||||
'error',
|
||||
`${file.name} not found`,
|
||||
'Run: npm install -g @kaitranntt/ccs --force'
|
||||
'Run: npm install -g @kaitranntt/ccs --force',
|
||||
{ status: 'ERROR', info: 'Not found' }
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -157,16 +205,48 @@ class Doctor {
|
||||
// Validate JSON
|
||||
try {
|
||||
const content = fs.readFileSync(file.path, 'utf8');
|
||||
JSON.parse(content);
|
||||
console.log(colored('[OK]', 'green'));
|
||||
this.results.addCheck(file.name, 'success');
|
||||
const config = JSON.parse(content);
|
||||
|
||||
// Extract useful info based on file type
|
||||
let info = 'Valid';
|
||||
let status = 'OK';
|
||||
|
||||
if (file.profile) {
|
||||
// For settings files, check if API key is configured
|
||||
const validation = DelegationValidator.validate(file.profile);
|
||||
|
||||
if (validation.valid) {
|
||||
info = 'Key configured';
|
||||
status = 'OK';
|
||||
} else if (validation.error && validation.error.includes('placeholder')) {
|
||||
info = 'Placeholder key (not configured)';
|
||||
status = 'WARN';
|
||||
} else {
|
||||
info = 'Valid JSON';
|
||||
status = 'OK';
|
||||
}
|
||||
}
|
||||
|
||||
const statusIcon = status === 'OK' ? colored('[OK]', 'green') : colored('[!]', 'yellow');
|
||||
|
||||
if (status === 'WARN') {
|
||||
spinner.warn(` ${file.name.padEnd(26)}${statusIcon} ${info}`);
|
||||
} else {
|
||||
spinner.succeed(` ${file.name.padEnd(26)}${statusIcon} ${info}`);
|
||||
}
|
||||
|
||||
this.results.addCheck(file.name, status === 'OK' ? 'success' : 'warning', null, null, {
|
||||
status: status,
|
||||
info: info
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(colored('[X]', 'red'));
|
||||
spinner.fail(` ${file.name.padEnd(26)}${colored('[X]', 'red')} Invalid JSON`);
|
||||
this.results.addCheck(
|
||||
file.name,
|
||||
'error',
|
||||
`Invalid JSON: ${e.message}`,
|
||||
`Backup and recreate: mv ${file.path} ${file.path}.backup && npm install -g @kaitranntt/ccs --force`
|
||||
`Backup and recreate: mv ${file.path} ${file.path}.backup && npm install -g @kaitranntt/ccs --force`,
|
||||
{ status: 'ERROR', info: 'Invalid JSON' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -176,12 +256,11 @@ class Doctor {
|
||||
* Check 4: Claude settings
|
||||
*/
|
||||
checkClaudeSettings() {
|
||||
process.stdout.write('[?] Checking ~/.claude/settings.json... ');
|
||||
|
||||
const spinner = ora('Checking ~/.claude/settings.json').start();
|
||||
const settingsPath = path.join(this.claudeDir, 'settings.json');
|
||||
|
||||
if (!fs.existsSync(settingsPath)) {
|
||||
console.log(colored('[!]', 'yellow'));
|
||||
spinner.warn(` ${'~/.claude/settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Not found`);
|
||||
this.results.addCheck(
|
||||
'Claude Settings',
|
||||
'warning',
|
||||
@@ -195,10 +274,10 @@ class Doctor {
|
||||
try {
|
||||
const content = fs.readFileSync(settingsPath, 'utf8');
|
||||
JSON.parse(content);
|
||||
console.log(colored('[OK]', 'green'));
|
||||
spinner.succeed(` ${'~/.claude/settings.json'.padEnd(26)}${colored('[OK]', 'green')}`);
|
||||
this.results.addCheck('Claude Settings', 'success');
|
||||
} catch (e) {
|
||||
console.log(colored('[!]', 'yellow'));
|
||||
spinner.warn(` ${'~/.claude/settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Invalid JSON`);
|
||||
this.results.addCheck(
|
||||
'Claude Settings',
|
||||
'warning',
|
||||
@@ -212,11 +291,11 @@ class Doctor {
|
||||
* Check 5: Profile configurations
|
||||
*/
|
||||
checkProfiles() {
|
||||
process.stdout.write('[?] Checking profiles... ');
|
||||
|
||||
const spinner = ora('Checking profiles').start();
|
||||
const configPath = path.join(this.ccsDir, 'config.json');
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
console.log(colored('[SKIP]', 'yellow'));
|
||||
spinner.info(` ${'Profiles'.padEnd(26)}${colored('[SKIP]', 'cyan')} config.json not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,22 +303,31 @@ class Doctor {
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
|
||||
if (!config.profiles || typeof config.profiles !== 'object') {
|
||||
console.log(colored('[X]', 'red'));
|
||||
spinner.fail(` ${'Profiles'.padEnd(26)}${colored('[X]', 'red')} Missing profiles object`);
|
||||
this.results.addCheck(
|
||||
'Profiles',
|
||||
'error',
|
||||
'config.json missing profiles object',
|
||||
'Run: npm install -g @kaitranntt/ccs --force'
|
||||
'Run: npm install -g @kaitranntt/ccs --force',
|
||||
{ status: 'ERROR', info: 'Missing profiles object' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const profileCount = Object.keys(config.profiles).length;
|
||||
console.log(colored('[OK]', 'green'), `(${profileCount} profiles)`);
|
||||
this.results.addCheck('Profiles', 'success', `${profileCount} profiles configured`);
|
||||
const profileNames = Object.keys(config.profiles).join(', ');
|
||||
|
||||
spinner.succeed(` ${'Profiles'.padEnd(26)}${colored('[OK]', 'green')} ${profileCount} configured (${profileNames})`);
|
||||
this.results.addCheck('Profiles', 'success', `${profileCount} profiles configured`, null, {
|
||||
status: 'OK',
|
||||
info: `${profileCount} configured (${profileNames.length > 30 ? profileNames.substring(0, 27) + '...' : profileNames})`
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(colored('[X]', 'red'));
|
||||
this.results.addCheck('Profiles', 'error', e.message);
|
||||
spinner.fail(` ${'Profiles'.padEnd(26)}${colored('[X]', 'red')} ${e.message}`);
|
||||
this.results.addCheck('Profiles', 'error', e.message, null, {
|
||||
status: 'ERROR',
|
||||
info: e.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,11 +335,11 @@ class Doctor {
|
||||
* Check 6: Instance directories (account-based profiles)
|
||||
*/
|
||||
checkInstances() {
|
||||
process.stdout.write('[?] Checking instances... ');
|
||||
|
||||
const spinner = ora('Checking instances').start();
|
||||
const instancesDir = path.join(this.ccsDir, 'instances');
|
||||
|
||||
if (!fs.existsSync(instancesDir)) {
|
||||
console.log(colored('[i]', 'cyan'), '(no account profiles)');
|
||||
spinner.info(` ${'Instances'.padEnd(26)}${colored('[i]', 'cyan')} No account profiles`);
|
||||
this.results.addCheck('Instances', 'success', 'No account profiles configured');
|
||||
return;
|
||||
}
|
||||
@@ -261,12 +349,12 @@ class Doctor {
|
||||
});
|
||||
|
||||
if (instances.length === 0) {
|
||||
console.log(colored('[i]', 'cyan'), '(no account profiles)');
|
||||
spinner.info(` ${'Instances'.padEnd(26)}${colored('[i]', 'cyan')} No account profiles`);
|
||||
this.results.addCheck('Instances', 'success', 'No account profiles');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(colored('[OK]', 'green'), `(${instances.length} instances)`);
|
||||
spinner.succeed(` ${'Instances'.padEnd(26)}${colored('[OK]', 'green')} ${instances.length} account profiles`);
|
||||
this.results.addCheck('Instances', 'success', `${instances.length} account profiles`);
|
||||
}
|
||||
|
||||
@@ -274,7 +362,7 @@ class Doctor {
|
||||
* Check 7: Delegation system
|
||||
*/
|
||||
checkDelegation() {
|
||||
process.stdout.write('[?] Checking delegation... ');
|
||||
const spinner = ora('Checking delegation').start();
|
||||
|
||||
// Check if delegation commands exist in ~/.ccs/.claude/commands/ccs/
|
||||
const ccsClaudeCommandsDir = path.join(this.ccsDir, '.claude', 'commands', 'ccs');
|
||||
@@ -282,12 +370,13 @@ class Doctor {
|
||||
const hasKimiCommand = fs.existsSync(path.join(ccsClaudeCommandsDir, 'kimi.md'));
|
||||
|
||||
if (!hasGlmCommand || !hasKimiCommand) {
|
||||
console.log(colored('[!]', 'yellow'), '(not installed)');
|
||||
spinner.warn(` ${'Delegation'.padEnd(26)}${colored('[!]', 'yellow')} Not installed`);
|
||||
this.results.addCheck(
|
||||
'Delegation',
|
||||
'warning',
|
||||
'Delegation commands not found',
|
||||
'Install with: npm install -g @kaitranntt/ccs --force'
|
||||
'Install with: npm install -g @kaitranntt/ccs --force',
|
||||
{ status: 'WARN', info: 'Not installed' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -304,21 +393,24 @@ class Doctor {
|
||||
}
|
||||
|
||||
if (readyProfiles.length === 0) {
|
||||
console.log(colored('[!]', 'yellow'), '(no profiles ready)');
|
||||
spinner.warn(` ${'Delegation'.padEnd(26)}${colored('[!]', 'yellow')} No profiles ready`);
|
||||
this.results.addCheck(
|
||||
'Delegation',
|
||||
'warning',
|
||||
'Delegation installed but no profiles configured',
|
||||
'Configure profiles with valid API keys (not placeholders)'
|
||||
'Configure profiles with valid API keys (not placeholders)',
|
||||
{ status: 'WARN', info: 'No profiles ready' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(colored('[OK]', 'green'), `(${readyProfiles.join(', ')} ready)`);
|
||||
spinner.succeed(` ${'Delegation'.padEnd(26)}${colored('[OK]', 'green')} ${readyProfiles.length} profiles ready (${readyProfiles.join(', ')})`);
|
||||
this.results.addCheck(
|
||||
'Delegation',
|
||||
'success',
|
||||
`${readyProfiles.length} profile(s) ready: ${readyProfiles.join(', ')}`
|
||||
`${readyProfiles.length} profile(s) ready: ${readyProfiles.join(', ')}`,
|
||||
null,
|
||||
{ status: 'OK', info: `${readyProfiles.length} profiles ready` }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -326,22 +418,25 @@ class Doctor {
|
||||
* Check 8: File permissions
|
||||
*/
|
||||
checkPermissions() {
|
||||
process.stdout.write('[?] Checking permissions... ');
|
||||
|
||||
const spinner = ora('Checking permissions').start();
|
||||
const testFile = path.join(this.ccsDir, '.permission-test');
|
||||
|
||||
try {
|
||||
fs.writeFileSync(testFile, 'test', 'utf8');
|
||||
fs.unlinkSync(testFile);
|
||||
console.log(colored('[OK]', 'green'));
|
||||
this.results.addCheck('Permissions', 'success');
|
||||
spinner.succeed(` ${'Permissions'.padEnd(26)}${colored('[OK]', 'green')} Write access verified`);
|
||||
this.results.addCheck('Permissions', 'success', null, null, {
|
||||
status: 'OK',
|
||||
info: 'Write access verified'
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(colored('[X]', 'red'));
|
||||
spinner.fail(` ${'Permissions'.padEnd(26)}${colored('[X]', 'red')} Cannot write to ~/.ccs/`);
|
||||
this.results.addCheck(
|
||||
'Permissions',
|
||||
'error',
|
||||
'Cannot write to ~/.ccs/',
|
||||
'Fix: sudo chown -R $USER ~/.ccs ~/.claude && chmod 755 ~/.ccs ~/.claude'
|
||||
'Fix: sudo chown -R $USER ~/.ccs ~/.claude && chmod 755 ~/.ccs ~/.claude',
|
||||
{ status: 'ERROR', info: 'Cannot write to ~/.ccs/' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -350,7 +445,7 @@ class Doctor {
|
||||
* Check 9: CCS symlinks to ~/.claude/
|
||||
*/
|
||||
checkCcsSymlinks() {
|
||||
process.stdout.write('[?] Checking CCS symlinks... ');
|
||||
const spinner = ora('Checking CCS symlinks').start();
|
||||
|
||||
try {
|
||||
const ClaudeSymlinkManager = require('../utils/claude-symlink-manager');
|
||||
@@ -358,24 +453,30 @@ class Doctor {
|
||||
const health = manager.checkHealth();
|
||||
|
||||
if (health.healthy) {
|
||||
console.log(colored('[OK]', 'green'));
|
||||
this.results.addCheck('CCS Symlinks', 'success', 'All CCS items properly symlinked');
|
||||
const itemCount = manager.ccsItems.length;
|
||||
spinner.succeed(` ${'CCS Symlinks'.padEnd(26)}${colored('[OK]', 'green')} ${itemCount}/${itemCount} items linked`);
|
||||
this.results.addCheck('CCS Symlinks', 'success', 'All CCS items properly symlinked', null, {
|
||||
status: 'OK',
|
||||
info: `${itemCount}/${itemCount} items synced`
|
||||
});
|
||||
} else {
|
||||
console.log(colored('[!]', 'yellow'));
|
||||
spinner.warn(` ${'CCS Symlinks'.padEnd(26)}${colored('[!]', 'yellow')} ${health.issues.length} issues found`);
|
||||
this.results.addCheck(
|
||||
'CCS Symlinks',
|
||||
'warning',
|
||||
health.issues.join(', '),
|
||||
'Run: ccs sync'
|
||||
'Run: ccs sync',
|
||||
{ status: 'WARN', info: `${health.issues.length} issues` }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(colored('[!]', 'yellow'));
|
||||
spinner.warn(` ${'CCS Symlinks'.padEnd(26)}${colored('[!]', 'yellow')} Could not check`);
|
||||
this.results.addCheck(
|
||||
'CCS Symlinks',
|
||||
'warning',
|
||||
'Could not check CCS symlinks: ' + e.message,
|
||||
'Run: ccs sync'
|
||||
'Run: ccs sync',
|
||||
{ status: 'WARN', info: 'Could not check' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -385,20 +486,43 @@ class Doctor {
|
||||
*/
|
||||
showReport() {
|
||||
console.log('');
|
||||
console.log(colored('═══════════════════════════════════════════', 'cyan'));
|
||||
console.log(colored('Health Check Report', 'bold'));
|
||||
console.log(colored('═══════════════════════════════════════════', 'cyan'));
|
||||
console.log('');
|
||||
|
||||
if (this.results.isHealthy() && !this.results.hasWarnings()) {
|
||||
console.log(colored('✓ All checks passed!', 'green'));
|
||||
console.log('');
|
||||
console.log('Your CCS installation is healthy.');
|
||||
console.log('');
|
||||
return;
|
||||
// Calculate exact table width to match header bars
|
||||
// colWidths: [20, 10, 35] = 65 + borders (4) = 69 total
|
||||
const tableWidth = 69;
|
||||
const headerBar = '═'.repeat(tableWidth);
|
||||
|
||||
console.log(colored(headerBar, 'cyan'));
|
||||
console.log(colored(' Health Check Summary', 'bold'));
|
||||
console.log(colored(headerBar, 'cyan'));
|
||||
|
||||
// Create summary table with detailed information
|
||||
const table = new Table({
|
||||
head: [colored('Component', 'cyan'), colored('Status', 'cyan'), colored('Details', 'cyan')],
|
||||
colWidths: [20, 10, 35],
|
||||
wordWrap: true,
|
||||
chars: {
|
||||
'top': '═', 'top-mid': '╤', 'top-left': '╔', 'top-right': '╗',
|
||||
'bottom': '═', 'bottom-mid': '╧', 'bottom-left': '╚', 'bottom-right': '╝',
|
||||
'left': '║', 'left-mid': '╟', 'mid': '─', 'mid-mid': '┼',
|
||||
'right': '║', 'right-mid': '╢', 'middle': '│'
|
||||
}
|
||||
});
|
||||
|
||||
// Populate table with collected details
|
||||
for (const [component, detail] of Object.entries(this.results.details)) {
|
||||
const statusColor = detail.status === 'OK' ? 'green' : detail.status === 'ERROR' ? 'red' : 'yellow';
|
||||
table.push([
|
||||
component,
|
||||
colored(detail.status, statusColor),
|
||||
detail.info || ''
|
||||
]);
|
||||
}
|
||||
|
||||
// Show errors
|
||||
console.log(table.toString());
|
||||
console.log('');
|
||||
|
||||
// Show errors and warnings if present
|
||||
if (this.results.hasErrors()) {
|
||||
console.log(colored('Errors:', 'red'));
|
||||
this.results.errors.forEach(err => {
|
||||
@@ -410,7 +534,6 @@ class Doctor {
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Show warnings
|
||||
if (this.results.hasWarnings()) {
|
||||
console.log(colored('Warnings:', 'yellow'));
|
||||
this.results.warnings.forEach(warn => {
|
||||
@@ -422,12 +545,14 @@ class Doctor {
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Summary
|
||||
if (this.results.hasErrors()) {
|
||||
console.log(colored('Status: Installation has errors', 'red'));
|
||||
// Final status
|
||||
if (this.results.isHealthy() && !this.results.hasWarnings()) {
|
||||
console.log(colored('[OK] All checks passed! Your CCS installation is healthy.', 'green'));
|
||||
} else if (this.results.hasErrors()) {
|
||||
console.log(colored('[X] Status: Installation has errors', 'red'));
|
||||
console.log('Run suggested fixes above to resolve issues.');
|
||||
} else {
|
||||
console.log(colored('Status: Installation healthy (warnings only)', 'green'));
|
||||
console.log(colored('[OK] Status: Installation healthy (warnings only)', 'green'));
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const ora = require('ora');
|
||||
const { colored } = require('./helpers');
|
||||
|
||||
/**
|
||||
* ClaudeDirInstaller - Manages copying .claude/ directory from package to ~/.ccs/.claude/
|
||||
@@ -18,8 +20,11 @@ class ClaudeDirInstaller {
|
||||
/**
|
||||
* Copy .claude/ directory from package to ~/.ccs/.claude/
|
||||
* @param {string} packageDir - Package installation directory (default: auto-detect)
|
||||
* @param {boolean} silent - Suppress spinner output
|
||||
*/
|
||||
install(packageDir) {
|
||||
install(packageDir, silent = false) {
|
||||
const spinner = silent ? null : ora('Copying .claude/ items to ~/.ccs/.claude/').start();
|
||||
|
||||
try {
|
||||
// Auto-detect package directory if not provided
|
||||
if (!packageDir) {
|
||||
@@ -30,21 +35,29 @@ class ClaudeDirInstaller {
|
||||
const packageClaudeDir = path.join(packageDir, '.claude');
|
||||
|
||||
if (!fs.existsSync(packageClaudeDir)) {
|
||||
console.log('[!] Package .claude/ directory not found');
|
||||
console.log(` Searched in: ${packageClaudeDir}`);
|
||||
console.log(' This may be a development installation');
|
||||
const msg = 'Package .claude/ directory not found';
|
||||
if (spinner) {
|
||||
spinner.warn(`[!] ${msg}`);
|
||||
console.log(` Searched in: ${packageClaudeDir}`);
|
||||
console.log(' This may be a development installation');
|
||||
} else {
|
||||
console.log(`[!] ${msg}`);
|
||||
console.log(` Searched in: ${packageClaudeDir}`);
|
||||
console.log(' This may be a development installation');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('[i] Installing CCS .claude/ items...');
|
||||
|
||||
// Remove old version before copying new one
|
||||
if (fs.existsSync(this.ccsClaudeDir)) {
|
||||
if (spinner) spinner.text = 'Removing old .claude/ items...';
|
||||
fs.rmSync(this.ccsClaudeDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// Use fs.cpSync for recursive copy (Node.js 16.7.0+)
|
||||
// Fallback to manual copy for older Node.js versions
|
||||
if (spinner) spinner.text = 'Copying .claude/ items...';
|
||||
|
||||
if (fs.cpSync) {
|
||||
fs.cpSync(packageClaudeDir, this.ccsClaudeDir, { recursive: true });
|
||||
} else {
|
||||
@@ -52,11 +65,25 @@ class ClaudeDirInstaller {
|
||||
this._copyDirRecursive(packageClaudeDir, this.ccsClaudeDir);
|
||||
}
|
||||
|
||||
console.log('[OK] Copied .claude/ items to ~/.ccs/.claude/');
|
||||
// Count files and directories
|
||||
const itemCount = this._countItems(this.ccsClaudeDir);
|
||||
const msg = `Copied .claude/ items (${itemCount.files} files, ${itemCount.dirs} directories)`;
|
||||
|
||||
if (spinner) {
|
||||
spinner.succeed(colored('[OK]', 'green') + ` ${msg}`);
|
||||
} else {
|
||||
console.log(`[OK] ${msg}`);
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.warn('[!] Failed to copy .claude/ directory:', err.message);
|
||||
console.warn(' CCS items may not be available');
|
||||
const msg = `Failed to copy .claude/ directory: ${err.message}`;
|
||||
if (spinner) {
|
||||
spinner.fail(colored('[!]', 'yellow') + ` ${msg}`);
|
||||
console.warn(' CCS items may not be available');
|
||||
} else {
|
||||
console.warn(`[!] ${msg}`);
|
||||
console.warn(' CCS items may not be available');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -90,6 +117,37 @@ class ClaudeDirInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count files and directories in a path
|
||||
* @param {string} dirPath - Directory to count
|
||||
* @returns {Object} { files: number, dirs: number }
|
||||
* @private
|
||||
*/
|
||||
_countItems(dirPath) {
|
||||
let files = 0;
|
||||
let dirs = 0;
|
||||
|
||||
const countRecursive = (p) => {
|
||||
const entries = fs.readdirSync(p, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
dirs++;
|
||||
countRecursive(path.join(p, entry.name));
|
||||
} else {
|
||||
files++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
countRecursive(dirPath);
|
||||
} catch (e) {
|
||||
// Ignore errors
|
||||
}
|
||||
|
||||
return { files, dirs };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if ~/.ccs/.claude/ exists and is valid
|
||||
* @returns {boolean} True if directory exists
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const ora = require('ora');
|
||||
const { colored } = require('./helpers');
|
||||
|
||||
/**
|
||||
* ClaudeSymlinkManager - Manages selective symlinks from ~/.ccs/.claude/ to ~/.claude/
|
||||
@@ -35,41 +37,62 @@ class ClaudeSymlinkManager {
|
||||
* Install CCS items to user's ~/.claude/ via selective symlinks
|
||||
* Safe: backs up existing files before creating symlinks
|
||||
*/
|
||||
install() {
|
||||
install(silent = false) {
|
||||
const spinner = silent ? null : ora('Installing CCS items to ~/.claude/').start();
|
||||
|
||||
// Ensure ~/.ccs/.claude/ exists (should be shipped with package)
|
||||
if (!fs.existsSync(this.ccsClaudeDir)) {
|
||||
console.log('[!] CCS .claude/ directory not found, skipping symlink installation');
|
||||
const msg = 'CCS .claude/ directory not found, skipping symlink installation';
|
||||
if (spinner) {
|
||||
spinner.warn(`[!] ${msg}`);
|
||||
} else {
|
||||
console.log(`[!] ${msg}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Create ~/.claude/ if missing
|
||||
if (!fs.existsSync(this.userClaudeDir)) {
|
||||
console.log('[i] Creating ~/.claude/ directory');
|
||||
if (!silent) {
|
||||
if (spinner) spinner.text = 'Creating ~/.claude/ directory';
|
||||
}
|
||||
fs.mkdirSync(this.userClaudeDir, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
|
||||
// Install each CCS item
|
||||
let installed = 0;
|
||||
for (const item of this.ccsItems) {
|
||||
this._installItem(item);
|
||||
if (!silent && spinner) {
|
||||
spinner.text = `Installing ${item.target}...`;
|
||||
}
|
||||
const result = this._installItem(item, silent);
|
||||
if (result) installed++;
|
||||
}
|
||||
|
||||
console.log('[OK] Delegation commands and skills installed to ~/.claude/');
|
||||
const msg = `${installed}/${this.ccsItems.length} items installed to ~/.claude/`;
|
||||
if (spinner) {
|
||||
spinner.succeed(colored('[OK]', 'green') + ` ${msg}`);
|
||||
} else {
|
||||
console.log(`[OK] ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a single CCS item with conflict handling
|
||||
* @param {Object} item - Item descriptor {source, target, type}
|
||||
* @param {boolean} silent - Suppress individual item messages
|
||||
* @returns {boolean} True if installed successfully
|
||||
* @private
|
||||
*/
|
||||
_installItem(item) {
|
||||
_installItem(item, silent = false) {
|
||||
const sourcePath = path.join(this.ccsClaudeDir, item.source);
|
||||
const targetPath = path.join(this.userClaudeDir, item.target);
|
||||
const targetDir = path.dirname(targetPath);
|
||||
|
||||
// Ensure source exists
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
console.log(`[!] Source not found: ${item.source}, skipping`);
|
||||
return;
|
||||
if (!silent) console.log(`[!] Source not found: ${item.source}, skipping`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create target parent directory if needed
|
||||
@@ -81,26 +104,30 @@ class ClaudeSymlinkManager {
|
||||
if (fs.existsSync(targetPath)) {
|
||||
// Check if it's already the correct symlink
|
||||
if (this._isOurSymlink(targetPath, sourcePath)) {
|
||||
return; // Already correct, skip
|
||||
return true; // Already correct, counts as success
|
||||
}
|
||||
|
||||
// Backup existing file/directory
|
||||
this._backupItem(targetPath);
|
||||
this._backupItem(targetPath, silent);
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
try {
|
||||
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
||||
fs.symlinkSync(sourcePath, targetPath, symlinkType);
|
||||
console.log(`[OK] Symlinked ${item.target}`);
|
||||
if (!silent) console.log(`[OK] Symlinked ${item.target}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
// Windows fallback: stub for now, full implementation in v4.2
|
||||
if (process.platform === 'win32') {
|
||||
console.log(`[!] Symlink failed for ${item.target} (Windows fallback deferred to v4.2)`);
|
||||
console.log(`[i] Enable Developer Mode or wait for next update`);
|
||||
if (!silent) {
|
||||
console.log(`[!] Symlink failed for ${item.target} (Windows fallback deferred to v4.2)`);
|
||||
console.log(`[i] Enable Developer Mode or wait for next update`);
|
||||
}
|
||||
} else {
|
||||
console.log(`[!] Failed to symlink ${item.target}: ${err.message}`);
|
||||
if (!silent) console.log(`[!] Failed to symlink ${item.target}: ${err.message}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +158,10 @@ class ClaudeSymlinkManager {
|
||||
/**
|
||||
* Backup existing item before replacing with symlink
|
||||
* @param {string} itemPath - Path to item to backup
|
||||
* @param {boolean} silent - Suppress backup messages
|
||||
* @private
|
||||
*/
|
||||
_backupItem(itemPath) {
|
||||
_backupItem(itemPath, silent = false) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
||||
const backupPath = `${itemPath}.backup-${timestamp}`;
|
||||
|
||||
@@ -147,9 +175,9 @@ class ClaudeSymlinkManager {
|
||||
}
|
||||
|
||||
fs.renameSync(itemPath, finalBackupPath);
|
||||
console.log(`[i] Backed up existing item to ${path.basename(finalBackupPath)}`);
|
||||
if (!silent) console.log(`[i] Backed up existing item to ${path.basename(finalBackupPath)}`);
|
||||
} catch (err) {
|
||||
console.log(`[!] Failed to backup ${itemPath}: ${err.message}`);
|
||||
if (!silent) console.log(`[!] Failed to backup ${itemPath}: ${err.message}`);
|
||||
throw err; // Don't proceed if backup fails
|
||||
}
|
||||
}
|
||||
@@ -230,8 +258,10 @@ class ClaudeSymlinkManager {
|
||||
* Same as install() but with explicit sync message
|
||||
*/
|
||||
sync() {
|
||||
console.log('[i] Syncing delegation commands and skills to ~/.claude/...');
|
||||
this.install();
|
||||
console.log('');
|
||||
console.log(colored('Syncing CCS Components...', 'cyan'));
|
||||
console.log('');
|
||||
this.install(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ $InstallMethod = if ($ScriptDir -and ((Test-Path "$ScriptDir\lib\ccs.ps1") -or (
|
||||
# IMPORTANT: Update this version when releasing new versions!
|
||||
# This hardcoded version is used for standalone installations (irm | iex)
|
||||
# For git installations, VERSION file is read if available
|
||||
$CcsVersion = "4.2.0"
|
||||
$CcsVersion = "4.3.0"
|
||||
|
||||
# Try to read VERSION file for git installations
|
||||
if ($ScriptDir) {
|
||||
|
||||
@@ -32,7 +32,7 @@ fi
|
||||
# IMPORTANT: Update this version when releasing new versions!
|
||||
# This hardcoded version is used for standalone installations (curl | bash)
|
||||
# For git installations, VERSION file is read if available
|
||||
CCS_VERSION="4.2.0"
|
||||
CCS_VERSION="4.3.0"
|
||||
|
||||
# Try to read VERSION file for git installations
|
||||
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
# Version (updated by scripts/bump-version.sh)
|
||||
CCS_VERSION="4.2.0"
|
||||
CCS_VERSION="4.3.0"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
||||
readonly PROFILES_JSON="$HOME/.ccs/profiles.json"
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ param(
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Version (updated by scripts/bump-version.sh)
|
||||
$CcsVersion = "4.2.0"
|
||||
$CcsVersion = "4.3.0"
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$ConfigFile = if ($env:CCS_CONFIG) { $env:CCS_CONFIG } else { "$env:USERPROFILE\.ccs\config.json" }
|
||||
$ProfilesJson = "$env:USERPROFILE\.ccs\profiles.json"
|
||||
|
||||
Generated
+351
-31
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@kaitranntt/ccs",
|
||||
"version": "3.4.2",
|
||||
"version": "4.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@kaitranntt/ccs",
|
||||
"version": "3.4.2",
|
||||
"version": "4.2.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"os": [
|
||||
@@ -14,6 +14,10 @@
|
||||
"linux",
|
||||
"win32"
|
||||
],
|
||||
"dependencies": {
|
||||
"cli-table3": "^0.6.5",
|
||||
"ora": "^5.4.1"
|
||||
},
|
||||
"bin": {
|
||||
"ccs": "bin/ccs.js"
|
||||
},
|
||||
@@ -24,6 +28,16 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@@ -70,7 +84,6 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
@@ -96,6 +109,37 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
@@ -113,6 +157,30 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||
@@ -130,7 +198,6 @@
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
@@ -147,7 +214,6 @@
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
@@ -172,6 +238,86 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"restore-cursor": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-spinners": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
|
||||
"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table3": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
|
||||
"integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "10.* || >= 12.*"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@colors/colors": "1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table3/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table3/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cli-table3/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table3/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
@@ -250,11 +396,19 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
@@ -267,7 +421,6 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
@@ -316,6 +469,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/defaults": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
|
||||
"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clone": "^1.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
|
||||
@@ -442,7 +607,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -458,11 +622,45 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-interactive": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
|
||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -492,7 +690,6 @@
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
|
||||
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -557,7 +754,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
||||
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.0",
|
||||
@@ -577,6 +773,15 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
@@ -647,6 +852,65 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/onetime": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ora": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
|
||||
"integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bl": "^4.1.0",
|
||||
"chalk": "^4.1.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-spinners": "^2.5.0",
|
||||
"is-interactive": "^1.0.0",
|
||||
"is-unicode-supported": "^0.1.0",
|
||||
"log-symbols": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wcwidth": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ora/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ora/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@@ -740,26 +1004,19 @@
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes/node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
@@ -785,6 +1042,45 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor/node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
@@ -831,6 +1127,15 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
@@ -964,6 +1269,21 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wcwidth": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
+5
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kaitranntt/ccs",
|
||||
"version": "4.2.0",
|
||||
"version": "4.3.0",
|
||||
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -57,6 +57,10 @@
|
||||
"prepare": "node scripts/check-executables.js",
|
||||
"postinstall": "node scripts/postinstall.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cli-table3": "^0.6.5",
|
||||
"ora": "^5.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^11.7.5"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user