mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 04:14:57 +00:00
feat(config): introduce shared settings.json across profiles
This commit is contained in:
1 parent
c4e5bcc40b
commit
739895a29e
12 files changed
+330
-97
No files matched your search
@@ -2,6 +2,24 @@
|
||||
|
||||
Format: [Keep a Changelog](https://keepachangelog.com/)
|
||||
|
||||
## [4.4.0] - 2025-11-23
|
||||
|
||||
### Changed
|
||||
- **BREAKING**: settings.json now shared across profiles via symlinks
|
||||
- Each profile previously had isolated settings.json
|
||||
- Now all profiles share ~/.claude/settings.json
|
||||
- Migration automatic on install (uses ~/.claude/settings.json)
|
||||
- Backups created: `<instance>/settings.json.pre-shared-migration`
|
||||
- Rollback: restore backup manually if needed
|
||||
|
||||
### Added
|
||||
- Doctor validates settings.json symlink integrity
|
||||
- Sync repairs broken settings.json symlinks
|
||||
- Migration from isolated to shared settings (automatic)
|
||||
|
||||
### Fixed
|
||||
- Consistent shared data architecture across all .claude/ items
|
||||
|
||||
## [4.3.10] - 2025-11-23
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -313,43 +313,21 @@ graph LR
|
||||
- Uses `CLAUDE_CONFIG_DIR` for isolated instances
|
||||
- Create with `ccs auth create <profile>`
|
||||
|
||||
### Shared Data (v3.1+)
|
||||
### Shared Data (v4.4+)
|
||||
|
||||
**CCS items (v4.1)**: Commands and skills symlinked from `~/.ccs/.claude/` to `~/.claude/` - **single source of truth with auto-propagation**.
|
||||
**Shared across instances** (`~/.ccs/shared/` symlinked to `~/.claude/`):
|
||||
- commands/ - Slash commands
|
||||
- skills/ - Agent skills
|
||||
- agents/ - Agent configs
|
||||
- **settings.json** - Claude CLI settings (v4.4+)
|
||||
|
||||
**Profile access**: `~/.ccs/shared/` symlinks to `~/.claude/` - **no duplication across profiles**.
|
||||
**Profile-specific**:
|
||||
- sessions/ - Conversation history
|
||||
- todolists/ - Todo lists
|
||||
- logs/ - Execution logs
|
||||
|
||||
```plaintext
|
||||
~/.ccs/
|
||||
├── .claude/ # CCS items (ships with package, v4.1)
|
||||
│ ├── commands/ccs/ # Delegation commands (/ccs, /ccs:continue)
|
||||
│ └── skills/ccs-delegation/ # AI decision framework (replaces deprecated agents)
|
||||
├── shared/ # Symlinks to ~/.claude/ (for profiles)
|
||||
│ ├── agents@ → ~/.claude/agents/
|
||||
│ ├── commands@ → ~/.claude/commands/
|
||||
│ └── skills@ → ~/.claude/skills/
|
||||
├── instances/ # Profile-specific data
|
||||
│ └── work/
|
||||
│ ├── agents@ → shared/agents/
|
||||
│ ├── commands@ → shared/commands/
|
||||
│ ├── skills@ → shared/skills/
|
||||
│ ├── settings.json # API keys, credentials
|
||||
│ ├── sessions/ # Conversation history
|
||||
│ └── ...
|
||||
|
||||
~/.claude/ # User's Claude directory
|
||||
├── commands/ccs@ → ~/.ccs/.claude/commands/ccs/ # Selective symlink
|
||||
├── skills/ccs-delegation@ → ~/.ccs/.claude/skills/ccs-delegation/
|
||||
# agents/ccs-delegator.md@ → ~/.ccs/.claude/agents/ccs-delegator.md # Deprecated in v4.3.2
|
||||
```
|
||||
|
||||
**Symlink Chain**: `work profile → ~/.ccs/shared/ → ~/.claude/ → ~/.ccs/.claude/` (CCS items)
|
||||
|
||||
| Type | Files |
|
||||
|:-----|:------|
|
||||
| **CCS items** | `~/.ccs/.claude/` (ships with package, selective symlinks to `~/.claude/`) |
|
||||
| **Shared** | `~/.ccs/shared/` (symlinks to `~/.claude/`) |
|
||||
| **Profile-specific** | `settings.json`, `sessions/`, `todolists/`, `logs/` |
|
||||
> [!NOTE]
|
||||
> **v4.4 Breaking Change**: settings.json now shared across profiles. Previously each profile had isolated settings. Migration is automatic on install using ~/.claude/settings.json as the authoritative source. Backups created: `<instance>/settings.json.pre-shared-migration`
|
||||
|
||||
> [!NOTE]
|
||||
> **Windows**: Symlink support requires Developer Mode (v4.2 will add copy fallback)
|
||||
|
||||
@@ -117,6 +117,7 @@ class Doctor {
|
||||
console.log(colored('System Health:', 'bold'));
|
||||
this.checkPermissions();
|
||||
this.checkCcsSymlinks();
|
||||
this.checkSettingsSymlinks();
|
||||
console.log('');
|
||||
|
||||
this.showReport();
|
||||
@@ -502,6 +503,127 @@ class Doctor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 10: settings.json symlinks
|
||||
*/
|
||||
checkSettingsSymlinks() {
|
||||
const spinner = ora('Checking settings.json symlinks').start();
|
||||
|
||||
try {
|
||||
const sharedDir = path.join(this.homedir, '.ccs', 'shared');
|
||||
const sharedSettings = path.join(sharedDir, 'settings.json');
|
||||
const claudeSettings = path.join(this.claudeDir, 'settings.json');
|
||||
|
||||
// Check shared settings exists and points to ~/.claude/
|
||||
if (!fs.existsSync(sharedSettings)) {
|
||||
spinner.warn(` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Not found`);
|
||||
this.results.addCheck(
|
||||
'Settings Symlinks',
|
||||
'warning',
|
||||
'Shared settings.json not found',
|
||||
'Run: ccs sync'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const sharedStats = fs.lstatSync(sharedSettings);
|
||||
if (!sharedStats.isSymbolicLink()) {
|
||||
spinner.warn(` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Not a symlink`);
|
||||
this.results.addCheck(
|
||||
'Settings Symlinks',
|
||||
'warning',
|
||||
'Shared settings.json is not a symlink',
|
||||
'Run: ccs sync'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const sharedTarget = fs.readlinkSync(sharedSettings);
|
||||
const resolvedShared = path.resolve(path.dirname(sharedSettings), sharedTarget);
|
||||
|
||||
if (resolvedShared !== claudeSettings) {
|
||||
spinner.warn(` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Wrong target`);
|
||||
this.results.addCheck(
|
||||
'Settings Symlinks',
|
||||
'warning',
|
||||
`Points to ${resolvedShared} instead of ${claudeSettings}`,
|
||||
'Run: ccs sync'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check each instance
|
||||
const instancesDir = path.join(this.ccsDir, 'instances');
|
||||
if (!fs.existsSync(instancesDir)) {
|
||||
spinner.succeed(` ${'settings.json'.padEnd(26)}${colored('[OK]', 'green')} Shared symlink valid`);
|
||||
this.results.addCheck('Settings Symlinks', 'success', 'Shared symlink valid', null, {
|
||||
status: 'OK',
|
||||
info: 'Shared symlink valid'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const instances = fs.readdirSync(instancesDir).filter(name => {
|
||||
return fs.statSync(path.join(instancesDir, name)).isDirectory();
|
||||
});
|
||||
|
||||
let broken = 0;
|
||||
for (const instance of instances) {
|
||||
const instancePath = path.join(instancesDir, instance);
|
||||
const instanceSettings = path.join(instancePath, 'settings.json');
|
||||
|
||||
if (!fs.existsSync(instanceSettings)) {
|
||||
broken++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = fs.lstatSync(instanceSettings);
|
||||
if (!stats.isSymbolicLink()) {
|
||||
broken++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const target = fs.readlinkSync(instanceSettings);
|
||||
const resolved = path.resolve(path.dirname(instanceSettings), target);
|
||||
|
||||
if (resolved !== sharedSettings) {
|
||||
broken++;
|
||||
}
|
||||
} catch (err) {
|
||||
broken++;
|
||||
}
|
||||
}
|
||||
|
||||
if (broken > 0) {
|
||||
spinner.warn(` ${'settings.json'.padEnd(26)}${colored('[!]', 'yellow')} ${broken} broken instance(s)`);
|
||||
this.results.addCheck(
|
||||
'Settings Symlinks',
|
||||
'warning',
|
||||
`${broken} instance(s) have broken symlinks`,
|
||||
'Run: ccs sync',
|
||||
{ status: 'WARN', info: `${broken} broken instance(s)` }
|
||||
);
|
||||
} else {
|
||||
spinner.succeed(` ${'settings.json'.padEnd(26)}${colored('[OK]', 'green')} ${instances.length} instance(s) valid`);
|
||||
this.results.addCheck('Settings Symlinks', 'success', 'All instance symlinks valid', null, {
|
||||
status: 'OK',
|
||||
info: `${instances.length} instance(s) valid`
|
||||
});
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
spinner.warn(` ${'settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Check failed`);
|
||||
this.results.addCheck(
|
||||
'Settings Symlinks',
|
||||
'warning',
|
||||
`Failed to check: ${err.message}`,
|
||||
'Run: ccs sync',
|
||||
{ status: 'WARN', info: 'Check failed' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show health check report
|
||||
*/
|
||||
|
||||
@@ -159,14 +159,9 @@ class InstanceManager {
|
||||
* @param {string} instancePath - Instance path
|
||||
*/
|
||||
_copyGlobalConfigs(instancePath) {
|
||||
const globalConfigDir = path.join(os.homedir(), '.claude');
|
||||
|
||||
// Copy settings.json only (commands/skills are now symlinked to shared/)
|
||||
const globalSettings = path.join(globalConfigDir, 'settings.json');
|
||||
if (fs.existsSync(globalSettings)) {
|
||||
const instanceSettings = path.join(instancePath, 'settings.json');
|
||||
fs.copyFileSync(globalSettings, instanceSettings);
|
||||
}
|
||||
// No longer needed - settings.json now symlinked via SharedManager
|
||||
// Keeping method for backward compatibility (empty implementation)
|
||||
// Can be removed in future major version
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,13 @@ class SharedManager {
|
||||
this.sharedDir = path.join(this.homeDir, '.ccs', 'shared');
|
||||
this.claudeDir = path.join(this.homeDir, '.claude');
|
||||
this.instancesDir = path.join(this.homeDir, '.ccs', 'instances');
|
||||
this.sharedDirs = ['commands', 'skills', 'agents', 'plugins'];
|
||||
this.sharedItems = [
|
||||
{ name: 'commands', type: 'directory' },
|
||||
{ name: 'skills', type: 'directory' },
|
||||
{ name: 'agents', type: 'directory' },
|
||||
{ name: 'plugins', type: 'directory' },
|
||||
{ name: 'settings.json', type: 'file' }
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,18 +80,23 @@ class SharedManager {
|
||||
}
|
||||
|
||||
// Create symlinks ~/.ccs/shared/* → ~/.claude/*
|
||||
for (const dir of this.sharedDirs) {
|
||||
const claudePath = path.join(this.claudeDir, dir);
|
||||
const sharedPath = path.join(this.sharedDir, dir);
|
||||
for (const item of this.sharedItems) {
|
||||
const claudePath = path.join(this.claudeDir, item.name);
|
||||
const sharedPath = path.join(this.sharedDir, item.name);
|
||||
|
||||
// Create directory in ~/.claude/ if missing
|
||||
// Create in ~/.claude/ if missing
|
||||
if (!fs.existsSync(claudePath)) {
|
||||
fs.mkdirSync(claudePath, { recursive: true, mode: 0o700 });
|
||||
if (item.type === 'directory') {
|
||||
fs.mkdirSync(claudePath, { recursive: true, mode: 0o700 });
|
||||
} else if (item.type === 'file') {
|
||||
// Create empty settings.json if missing
|
||||
fs.writeFileSync(claudePath, JSON.stringify({}, null, 2), 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
// Check for circular symlink
|
||||
if (this._detectCircularSymlink(claudePath, sharedPath)) {
|
||||
console.log(`[!] Skipping ${dir}: circular symlink detected`);
|
||||
console.log(`[!] Skipping ${item.name}: circular symlink detected`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -104,18 +115,27 @@ class SharedManager {
|
||||
// Continue to recreate
|
||||
}
|
||||
|
||||
// Remove existing directory/link
|
||||
fs.rmSync(sharedPath, { recursive: true, force: true });
|
||||
// Remove existing file/directory/link
|
||||
if (item.type === 'directory') {
|
||||
fs.rmSync(sharedPath, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(sharedPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
try {
|
||||
fs.symlinkSync(claudePath, sharedPath, 'dir');
|
||||
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
||||
fs.symlinkSync(claudePath, sharedPath, symlinkType);
|
||||
} catch (err) {
|
||||
// Windows fallback: copy directory
|
||||
// Windows fallback: copy
|
||||
if (process.platform === 'win32') {
|
||||
this._copyDirectoryFallback(claudePath, sharedPath);
|
||||
console.log(`[!] Symlink failed for ${dir}, copied instead (enable Developer Mode)`);
|
||||
if (item.type === 'directory') {
|
||||
this._copyDirectoryFallback(claudePath, sharedPath);
|
||||
} else if (item.type === 'file') {
|
||||
fs.copyFileSync(claudePath, sharedPath);
|
||||
}
|
||||
console.log(`[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)`);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
@@ -130,23 +150,32 @@ class SharedManager {
|
||||
linkSharedDirectories(instancePath) {
|
||||
this.ensureSharedDirectories();
|
||||
|
||||
for (const dir of this.sharedDirs) {
|
||||
const linkPath = path.join(instancePath, dir);
|
||||
const targetPath = path.join(this.sharedDir, dir);
|
||||
for (const item of this.sharedItems) {
|
||||
const linkPath = path.join(instancePath, item.name);
|
||||
const targetPath = path.join(this.sharedDir, item.name);
|
||||
|
||||
// Remove existing directory/link
|
||||
// Remove existing file/directory/link
|
||||
if (fs.existsSync(linkPath)) {
|
||||
fs.rmSync(linkPath, { recursive: true, force: true });
|
||||
if (item.type === 'directory') {
|
||||
fs.rmSync(linkPath, { recursive: true, force: true });
|
||||
} else {
|
||||
fs.unlinkSync(linkPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
try {
|
||||
fs.symlinkSync(targetPath, linkPath, 'dir');
|
||||
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
||||
fs.symlinkSync(targetPath, linkPath, symlinkType);
|
||||
} catch (err) {
|
||||
// Windows fallback
|
||||
if (process.platform === 'win32') {
|
||||
this._copyDirectoryFallback(targetPath, linkPath);
|
||||
console.log(`[!] Symlink failed for ${dir}, copied instead (enable Developer Mode)`);
|
||||
if (item.type === 'directory') {
|
||||
this._copyDirectoryFallback(targetPath, linkPath);
|
||||
} else if (item.type === 'file') {
|
||||
fs.copyFileSync(targetPath, linkPath);
|
||||
}
|
||||
console.log(`[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)`);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
@@ -179,49 +208,56 @@ class SharedManager {
|
||||
}
|
||||
|
||||
// Copy user modifications from ~/.ccs/shared/ to ~/.claude/
|
||||
for (const dir of this.sharedDirs) {
|
||||
const sharedPath = path.join(this.sharedDir, dir);
|
||||
const claudePath = path.join(this.claudeDir, dir);
|
||||
for (const item of this.sharedItems) {
|
||||
const sharedPath = path.join(this.sharedDir, item.name);
|
||||
const claudePath = path.join(this.claudeDir, item.name);
|
||||
|
||||
if (!fs.existsSync(sharedPath)) continue;
|
||||
|
||||
try {
|
||||
const stats = fs.lstatSync(sharedPath);
|
||||
if (!stats.isDirectory()) continue;
|
||||
} catch (err) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create claude dir if missing
|
||||
if (!fs.existsSync(claudePath)) {
|
||||
fs.mkdirSync(claudePath, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
// Handle directories
|
||||
if (item.type === 'directory' && stats.isDirectory()) {
|
||||
// Create claude dir if missing
|
||||
if (!fs.existsSync(claudePath)) {
|
||||
fs.mkdirSync(claudePath, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
|
||||
// Copy files from shared to claude (preserve user modifications)
|
||||
try {
|
||||
const entries = fs.readdirSync(sharedPath, { withFileTypes: true });
|
||||
let copied = 0;
|
||||
// Copy files from shared to claude (preserve user modifications)
|
||||
const entries = fs.readdirSync(sharedPath, { withFileTypes: true });
|
||||
let copied = 0;
|
||||
|
||||
for (const entry of entries) {
|
||||
const src = path.join(sharedPath, entry.name);
|
||||
const dest = path.join(claudePath, entry.name);
|
||||
for (const entry of entries) {
|
||||
const src = path.join(sharedPath, entry.name);
|
||||
const dest = path.join(claudePath, entry.name);
|
||||
|
||||
// Skip if already exists in claude
|
||||
if (fs.existsSync(dest)) continue;
|
||||
// Skip if already exists in claude
|
||||
if (fs.existsSync(dest)) continue;
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
fs.cpSync(src, dest, { recursive: true });
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
if (entry.isDirectory()) {
|
||||
fs.cpSync(src, dest, { recursive: true });
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
}
|
||||
copied++;
|
||||
}
|
||||
|
||||
if (copied > 0) {
|
||||
console.log(`[OK] Migrated ${copied} ${item.name} to ~/.claude/${item.name}`);
|
||||
}
|
||||
copied++;
|
||||
}
|
||||
|
||||
if (copied > 0) {
|
||||
console.log(`[OK] Migrated ${copied} ${dir} to ~/.claude/${dir}`);
|
||||
// Handle files (settings.json)
|
||||
else if (item.type === 'file' && stats.isFile()) {
|
||||
// Only copy if ~/.claude/ version doesn't exist
|
||||
if (!fs.existsSync(claudePath)) {
|
||||
fs.copyFileSync(sharedPath, claudePath);
|
||||
console.log(`[OK] Migrated ${item.name} to ~/.claude/${item.name}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`[!] Failed to migrate ${dir}: ${err.message}`);
|
||||
console.log(`[!] Failed to migrate ${item.name}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +287,87 @@ class SharedManager {
|
||||
console.log('[OK] Migration to v3.2.0 complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate existing instances from isolated to shared settings.json (v4.4+)
|
||||
* Runs once on upgrade
|
||||
*/
|
||||
migrateToSharedSettings() {
|
||||
console.log('[i] Migrating instances to shared settings.json...');
|
||||
|
||||
// Ensure ~/.claude/settings.json exists (authoritative source)
|
||||
const claudeSettings = path.join(this.claudeDir, 'settings.json');
|
||||
if (!fs.existsSync(claudeSettings)) {
|
||||
// Create empty settings if missing
|
||||
fs.writeFileSync(claudeSettings, JSON.stringify({}, null, 2), 'utf8');
|
||||
console.log('[i] Created ~/.claude/settings.json');
|
||||
}
|
||||
|
||||
// Ensure shared settings.json symlink exists
|
||||
this.ensureSharedDirectories();
|
||||
|
||||
// Migrate each instance
|
||||
if (!fs.existsSync(this.instancesDir)) {
|
||||
console.log('[i] No instances to migrate');
|
||||
return;
|
||||
}
|
||||
|
||||
const instances = fs.readdirSync(this.instancesDir).filter(name => {
|
||||
const instancePath = path.join(this.instancesDir, name);
|
||||
return fs.statSync(instancePath).isDirectory();
|
||||
});
|
||||
|
||||
let migrated = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for (const instance of instances) {
|
||||
const instancePath = path.join(this.instancesDir, instance);
|
||||
const instanceSettings = path.join(instancePath, 'settings.json');
|
||||
|
||||
try {
|
||||
// Check if already symlink
|
||||
if (fs.existsSync(instanceSettings)) {
|
||||
const stats = fs.lstatSync(instanceSettings);
|
||||
if (stats.isSymbolicLink()) {
|
||||
skipped++;
|
||||
continue; // Already migrated
|
||||
}
|
||||
|
||||
// Backup existing settings
|
||||
const backup = instanceSettings + '.pre-shared-migration';
|
||||
if (!fs.existsSync(backup)) {
|
||||
fs.copyFileSync(instanceSettings, backup);
|
||||
console.log(`[i] Backed up ${instance}/settings.json`);
|
||||
}
|
||||
|
||||
// Remove old settings.json
|
||||
fs.unlinkSync(instanceSettings);
|
||||
}
|
||||
|
||||
// Create symlink via SharedManager
|
||||
const sharedSettings = path.join(this.sharedDir, 'settings.json');
|
||||
|
||||
try {
|
||||
fs.symlinkSync(sharedSettings, instanceSettings, 'file');
|
||||
migrated++;
|
||||
} catch (err) {
|
||||
// Windows fallback
|
||||
if (process.platform === 'win32') {
|
||||
fs.copyFileSync(sharedSettings, instanceSettings);
|
||||
console.log(`[!] Symlink failed for ${instance}, copied instead`);
|
||||
migrated++;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log(`[!] Failed to migrate ${instance}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[OK] Migrated ${migrated} instance(s), skipped ${skipped}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy directory as fallback (Windows without Developer Mode)
|
||||
* @param {string} src - Source directory
|
||||
|
||||
@@ -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.3.10"
|
||||
$CcsVersion = "4.4.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.3.10"
|
||||
CCS_VERSION="4.4.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.3.10"
|
||||
CCS_VERSION="4.4.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.3.10"
|
||||
$CcsVersion = "4.4.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"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kaitranntt/ccs",
|
||||
"version": "4.3.10",
|
||||
"version": "4.4.0",
|
||||
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -97,6 +97,9 @@ function createConfigFiles() {
|
||||
const sharedManager = new SharedManager();
|
||||
sharedManager.migrateFromV311();
|
||||
sharedManager.ensureSharedDirectories();
|
||||
|
||||
// Run v4.4 migration: Migrate instances to shared settings.json
|
||||
sharedManager.migrateToSharedSettings();
|
||||
} catch (err) {
|
||||
console.warn('[!] Migration warning:', err.message);
|
||||
console.warn(' Migration will retry on next run');
|
||||
|
||||
Reference in new issue
Block a user