@kaitranntt/ccs 3.1.0 → 3.1.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/VERSION +1 -1
- package/bin/ccs.js +0 -5
- package/bin/shared-manager.js +100 -40
- package/lib/ccs +1 -4
- package/lib/ccs.ps1 +1 -4
- package/package.json +1 -1
- package/scripts/postinstall.js +12 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.1.
|
|
1
|
+
3.1.1
|
package/bin/ccs.js
CHANGED
|
@@ -262,11 +262,6 @@ async function main() {
|
|
|
262
262
|
recovery.showRecoveryHints();
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
// Run migration to shared structure (Phase 1: idempotent)
|
|
266
|
-
const SharedManager = require('./shared-manager');
|
|
267
|
-
const sharedManager = new SharedManager();
|
|
268
|
-
sharedManager.migrateToSharedStructure();
|
|
269
|
-
|
|
270
265
|
// Detect profile
|
|
271
266
|
const { profile, remainingArgs } = detectProfile(args);
|
|
272
267
|
|
package/bin/shared-manager.js
CHANGED
|
@@ -68,52 +68,100 @@ class SharedManager {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Check if migration is needed
|
|
73
|
+
* @returns {boolean}
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
_needsMigration() {
|
|
77
|
+
// If shared dir doesn't exist, migration needed
|
|
78
|
+
if (!fs.existsSync(this.sharedDir)) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if ALL shared directories are empty
|
|
83
|
+
const allEmpty = this.sharedDirs.every(dir => {
|
|
84
|
+
const dirPath = path.join(this.sharedDir, dir);
|
|
85
|
+
if (!fs.existsSync(dirPath)) return true;
|
|
86
|
+
try {
|
|
87
|
+
const files = fs.readdirSync(dirPath);
|
|
88
|
+
return files.length === 0;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return true; // If can't read, assume empty
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return allEmpty;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Perform migration from ~/.claude/ to ~/.ccs/shared/
|
|
99
|
+
* @returns {object} { commands: N, skills: N, agents: N }
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
_performMigration() {
|
|
103
|
+
const stats = { commands: 0, skills: 0, agents: 0 };
|
|
104
|
+
const claudeDir = path.join(this.homeDir, '.claude');
|
|
105
|
+
|
|
106
|
+
if (!fs.existsSync(claudeDir)) {
|
|
107
|
+
return stats; // No content to migrate
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Migrate commands
|
|
111
|
+
const commandsPath = path.join(claudeDir, 'commands');
|
|
112
|
+
if (fs.existsSync(commandsPath)) {
|
|
113
|
+
const result = this._copyDirectory(commandsPath, path.join(this.sharedDir, 'commands'));
|
|
114
|
+
stats.commands = result.copied;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Migrate skills
|
|
118
|
+
const skillsPath = path.join(claudeDir, 'skills');
|
|
119
|
+
if (fs.existsSync(skillsPath)) {
|
|
120
|
+
const result = this._copyDirectory(skillsPath, path.join(this.sharedDir, 'skills'));
|
|
121
|
+
stats.skills = result.copied;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Migrate agents
|
|
125
|
+
const agentsPath = path.join(claudeDir, 'agents');
|
|
126
|
+
if (fs.existsSync(agentsPath)) {
|
|
127
|
+
const result = this._copyDirectory(agentsPath, path.join(this.sharedDir, 'agents'));
|
|
128
|
+
stats.agents = result.copied;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return stats;
|
|
132
|
+
}
|
|
133
|
+
|
|
71
134
|
/**
|
|
72
135
|
* Migrate existing instances to shared structure
|
|
73
136
|
* Idempotent: Safe to run multiple times
|
|
74
137
|
*/
|
|
75
138
|
migrateToSharedStructure() {
|
|
76
|
-
|
|
77
|
-
const needsMigration = !fs.existsSync(this.sharedDir) ||
|
|
78
|
-
this.sharedDirs.every(dir => {
|
|
79
|
-
const dirPath = path.join(this.sharedDir, dir);
|
|
80
|
-
if (!fs.existsSync(dirPath)) return true;
|
|
81
|
-
try {
|
|
82
|
-
const files = fs.readdirSync(dirPath);
|
|
83
|
-
return files.length === 0; // Empty directory needs migration
|
|
84
|
-
} catch (err) {
|
|
85
|
-
return true; // If we can't read it, assume it needs migration
|
|
86
|
-
}
|
|
87
|
-
});
|
|
139
|
+
console.log('[i] Checking for content migration...');
|
|
88
140
|
|
|
89
|
-
if
|
|
90
|
-
|
|
141
|
+
// Check if migration is needed
|
|
142
|
+
if (!this._needsMigration()) {
|
|
143
|
+
console.log('[OK] Migration not needed (shared dirs have content)');
|
|
144
|
+
return;
|
|
91
145
|
}
|
|
92
146
|
|
|
147
|
+
console.log('[i] Migrating ~/.claude/ content to ~/.ccs/shared/...');
|
|
148
|
+
|
|
93
149
|
// Create shared directories
|
|
94
150
|
this.ensureSharedDirectories();
|
|
95
151
|
|
|
96
|
-
//
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this._copyDirectory(skillsPath, path.join(this.sharedDir, 'skills'));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Copy agents to shared (if exists)
|
|
113
|
-
const agentsPath = path.join(claudeDir, 'agents');
|
|
114
|
-
if (fs.existsSync(agentsPath)) {
|
|
115
|
-
this._copyDirectory(agentsPath, path.join(this.sharedDir, 'agents'));
|
|
116
|
-
}
|
|
152
|
+
// Perform migration
|
|
153
|
+
const stats = this._performMigration();
|
|
154
|
+
|
|
155
|
+
// Show results
|
|
156
|
+
const total = stats.commands + stats.skills + stats.agents;
|
|
157
|
+
if (total === 0) {
|
|
158
|
+
console.log('[OK] No content to migrate (empty ~/.claude/)');
|
|
159
|
+
} else {
|
|
160
|
+
const parts = [];
|
|
161
|
+
if (stats.commands > 0) parts.push(`${stats.commands} commands`);
|
|
162
|
+
if (stats.skills > 0) parts.push(`${stats.skills} skills`);
|
|
163
|
+
if (stats.agents > 0) parts.push(`${stats.agents} agents`);
|
|
164
|
+
console.log(`[OK] Migrated ${parts.join(', ')}`);
|
|
117
165
|
}
|
|
118
166
|
|
|
119
167
|
// Update all instances to use symlinks
|
|
@@ -127,19 +175,18 @@ class SharedManager {
|
|
|
127
175
|
}
|
|
128
176
|
}
|
|
129
177
|
}
|
|
130
|
-
|
|
131
|
-
console.log('[OK] Migrated to shared structure');
|
|
132
178
|
}
|
|
133
179
|
|
|
134
180
|
/**
|
|
135
|
-
* Copy directory recursively (
|
|
181
|
+
* Copy directory recursively (SAFE: preserves existing files)
|
|
136
182
|
* @param {string} src - Source directory
|
|
137
183
|
* @param {string} dest - Destination directory
|
|
184
|
+
* @returns {object} { copied: N, skipped: N }
|
|
138
185
|
* @private
|
|
139
186
|
*/
|
|
140
187
|
_copyDirectory(src, dest) {
|
|
141
188
|
if (!fs.existsSync(src)) {
|
|
142
|
-
return;
|
|
189
|
+
return { copied: 0, skipped: 0 };
|
|
143
190
|
}
|
|
144
191
|
|
|
145
192
|
if (!fs.existsSync(dest)) {
|
|
@@ -147,17 +194,30 @@ class SharedManager {
|
|
|
147
194
|
}
|
|
148
195
|
|
|
149
196
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
197
|
+
let copied = 0;
|
|
198
|
+
let skipped = 0;
|
|
150
199
|
|
|
151
200
|
for (const entry of entries) {
|
|
152
201
|
const srcPath = path.join(src, entry.name);
|
|
153
202
|
const destPath = path.join(dest, entry.name);
|
|
154
203
|
|
|
204
|
+
// SAFETY: Skip if destination exists (preserve user modifications)
|
|
205
|
+
if (fs.existsSync(destPath)) {
|
|
206
|
+
skipped++;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
155
210
|
if (entry.isDirectory()) {
|
|
156
|
-
this._copyDirectory(srcPath, destPath);
|
|
211
|
+
const stats = this._copyDirectory(srcPath, destPath);
|
|
212
|
+
copied += stats.copied;
|
|
213
|
+
skipped += stats.skipped;
|
|
157
214
|
} else {
|
|
158
215
|
fs.copyFileSync(srcPath, destPath);
|
|
216
|
+
copied++;
|
|
159
217
|
}
|
|
160
218
|
}
|
|
219
|
+
|
|
220
|
+
return { copied, skipped };
|
|
161
221
|
}
|
|
162
222
|
}
|
|
163
223
|
|
package/lib/ccs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
# Version (updated by scripts/bump-version.sh)
|
|
5
|
-
CCS_VERSION="3.1.
|
|
5
|
+
CCS_VERSION="3.1.1"
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
7
|
readonly CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
8
8
|
readonly PROFILES_JSON="$HOME/.ccs/profiles.json"
|
|
@@ -1010,9 +1010,6 @@ auto_recover || {
|
|
|
1010
1010
|
exit 1
|
|
1011
1011
|
}
|
|
1012
1012
|
|
|
1013
|
-
# Run migration to shared structure (Phase 1: idempotent)
|
|
1014
|
-
migrate_to_shared_structure
|
|
1015
|
-
|
|
1016
1013
|
# Smart profile detection: if first arg starts with '-', it's a flag not a profile
|
|
1017
1014
|
if [[ $# -eq 0 ]] || [[ "${1}" =~ ^- ]]; then
|
|
1018
1015
|
# No args or first arg is a flag → use default profile
|
package/lib/ccs.ps1
CHANGED
|
@@ -12,7 +12,7 @@ param(
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
13
|
|
|
14
14
|
# Version (updated by scripts/bump-version.sh)
|
|
15
|
-
$CcsVersion = "3.1.
|
|
15
|
+
$CcsVersion = "3.1.1"
|
|
16
16
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
17
17
|
$ConfigFile = if ($env:CCS_CONFIG) { $env:CCS_CONFIG } else { "$env:USERPROFILE\.ccs\config.json" }
|
|
18
18
|
$ProfilesJson = "$env:USERPROFILE\.ccs\profiles.json"
|
|
@@ -1000,9 +1000,6 @@ if (-not (Invoke-AutoRecovery)) {
|
|
|
1000
1000
|
exit 1
|
|
1001
1001
|
}
|
|
1002
1002
|
|
|
1003
|
-
# Run migration to shared structure (Phase 1: idempotent)
|
|
1004
|
-
Migrate-SharedStructure
|
|
1005
|
-
|
|
1006
1003
|
# Smart profile detection: if first arg starts with '-', it's a flag not a profile
|
|
1007
1004
|
if ($RemainingArgs.Count -eq 0 -or $RemainingArgs[0] -match '^-') {
|
|
1008
1005
|
# No args or first arg is a flag → use default profile
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -89,6 +89,18 @@ function createConfigFiles() {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// Migrate from ~/.claude/ to ~/.ccs/shared/ (v3.1.1)
|
|
93
|
+
console.log('');
|
|
94
|
+
try {
|
|
95
|
+
const SharedManager = require('../bin/shared-manager');
|
|
96
|
+
const sharedManager = new SharedManager();
|
|
97
|
+
sharedManager.migrateToSharedStructure();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.warn('[!] Migration warning:', err.message);
|
|
100
|
+
console.warn(' You can manually copy files from ~/.claude/ to ~/.ccs/shared/');
|
|
101
|
+
}
|
|
102
|
+
console.log('');
|
|
103
|
+
|
|
92
104
|
// Create config.json if missing
|
|
93
105
|
const configPath = path.join(ccsDir, 'config.json');
|
|
94
106
|
if (!fs.existsSync(configPath)) {
|