claude-switch-profile 1.4.21 → 1.4.23
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/Formula/claude-switch-profile.rb +2 -2
- package/README.md +17 -1
- package/bin/csp.js +9 -1
- package/package.json +1 -1
- package/src/commands/create.js +2 -2
- package/src/commands/update.js +116 -0
- package/src/commands/use.js +4 -13
- package/src/constants.js +1 -1
- package/src/file-operations.js +1 -36
- package/src/item-manager.js +7 -60
|
@@ -3,8 +3,8 @@ require "language/node"
|
|
|
3
3
|
class ClaudeSwitchProfile < Formula
|
|
4
4
|
desc "CLI tool for managing multiple Claude Code profiles"
|
|
5
5
|
homepage "https://github.com/ThanhThi2895/claude-switch-profile"
|
|
6
|
-
url "https://registry.npmjs.org/claude-switch-profile/-/claude-switch-profile-1.4.
|
|
7
|
-
sha256 "
|
|
6
|
+
url "https://registry.npmjs.org/claude-switch-profile/-/claude-switch-profile-1.4.22.tgz"
|
|
7
|
+
sha256 "4d828740a0d3c6e77093ca074686ab71412589c6eaa987a0d2e92002294e1a95"
|
|
8
8
|
license "MIT"
|
|
9
9
|
|
|
10
10
|
depends_on "node"
|
package/README.md
CHANGED
|
@@ -39,7 +39,15 @@ curl -fsSL https://raw.githubusercontent.com/ThanhThi2895/claude-switch-profile/
|
|
|
39
39
|
Leverages Homebrew's own managed cellars to isolate the Node engine:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
brew tap ThanhThi2895/claude-switch-profile
|
|
42
|
+
brew tap ThanhThi2895/claude-switch-profile https://github.com/ThanhThi2895/claude-switch-profile
|
|
43
|
+
brew install claude-switch-profile
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you previously hit a tap resolution error, reset then tap again:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
brew untap ThanhThi2895/claude-switch-profile 2>/dev/null || true
|
|
50
|
+
brew tap ThanhThi2895/claude-switch-profile https://github.com/ThanhThi2895/claude-switch-profile
|
|
43
51
|
brew install claude-switch-profile
|
|
44
52
|
```
|
|
45
53
|
|
|
@@ -101,6 +109,7 @@ csp
|
|
|
101
109
|
| Command | Description |
|
|
102
110
|
|---|---|
|
|
103
111
|
| `csp` / `csp select` | Interactive profile selector (default) |
|
|
112
|
+
| `csp -v` / `csp --version` | Print the current CSP version |
|
|
104
113
|
| `csp init` | Initialize and capture current state as `default` profile |
|
|
105
114
|
| `csp create <name>` | Create a new profile (`--from`, `--source`, `-d` options) |
|
|
106
115
|
| `csp launch <name>` | Launch isolated Claude session for a profile |
|
|
@@ -116,8 +125,13 @@ csp
|
|
|
116
125
|
| `csp import <file>` | Import profile from archive |
|
|
117
126
|
| `csp delete <name>` | Delete a profile |
|
|
118
127
|
| `csp deactivate` | Switch back to `default` profile |
|
|
128
|
+
| `csp update [--method <npm\|brew\|standalone>]` | Update CSP (auto-detect install method by default) |
|
|
119
129
|
| `csp uninstall --method <npm\|brew\|standalone>` | Uninstall csp CLI and keep all profiles |
|
|
120
130
|
|
|
131
|
+
> `csp -v` is a short alias for `csp --version` and prints the same semver.
|
|
132
|
+
>
|
|
133
|
+
> `csp update` auto-detects install method (npm/brew/standalone). Use `--method` to override explicitly.
|
|
134
|
+
>
|
|
121
135
|
> 📖 **Full command reference with all options and detailed behavior:** [docs/commands-reference.md](docs/commands-reference.md)
|
|
122
136
|
|
|
123
137
|
## Two Launch Modes
|
|
@@ -143,6 +157,8 @@ csp use work
|
|
|
143
157
|
```
|
|
144
158
|
|
|
145
159
|
- Mutates `~/.claude` directly
|
|
160
|
+
- Saves current active profile snapshot by copy (unless `--no-save`)
|
|
161
|
+
- Restores target profile snapshot into `~/.claude` by copy (profile snapshot is not consumed)
|
|
146
162
|
- Updates `.active` marker
|
|
147
163
|
- Requires Claude restart
|
|
148
164
|
|
package/bin/csp.js
CHANGED
|
@@ -16,6 +16,7 @@ import { importCommand } from '../src/commands/import.js';
|
|
|
16
16
|
import { diffCommand } from '../src/commands/diff.js';
|
|
17
17
|
import { initCommand } from '../src/commands/init.js';
|
|
18
18
|
import { uninstallCommand } from '../src/commands/uninstall.js';
|
|
19
|
+
import { updateCommand } from '../src/commands/update.js';
|
|
19
20
|
import { launchCommand, execCommand } from '../src/commands/launch.js';
|
|
20
21
|
import { deactivateCommand } from '../src/commands/deactivate.js';
|
|
21
22
|
import { toggleCommand } from '../src/commands/toggle.js';
|
|
@@ -30,7 +31,7 @@ const program = new Command();
|
|
|
30
31
|
program
|
|
31
32
|
.name('csp')
|
|
32
33
|
.description('Claude Switch Profile — manage multiple Claude Code configurations')
|
|
33
|
-
.version(pkg.version)
|
|
34
|
+
.version(pkg.version, '-v, --version')
|
|
34
35
|
.enablePositionalOptions();
|
|
35
36
|
|
|
36
37
|
program
|
|
@@ -157,4 +158,11 @@ program
|
|
|
157
158
|
.option('--method <method>', 'Install method: npm | brew | standalone')
|
|
158
159
|
.action(uninstallCommand);
|
|
159
160
|
|
|
161
|
+
program
|
|
162
|
+
.command('update')
|
|
163
|
+
.description('Update csp CLI while keeping all profile data intact')
|
|
164
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
165
|
+
.option('--method <method>', 'Install method: npm | brew | standalone')
|
|
166
|
+
.action(updateCommand);
|
|
167
|
+
|
|
160
168
|
program.parse();
|
package/package.json
CHANGED
package/src/commands/create.js
CHANGED
|
@@ -81,7 +81,7 @@ export const createCommand = (name, options) => {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// MANAGED_FILES → empty file stubs (CLAUDE.md, statusline.*, .luna.json)
|
|
84
|
-
// These must exist so
|
|
84
|
+
// These must exist so restoreItems can copy profile snapshots into ~/.claude
|
|
85
85
|
const managedFiles = MANAGED_ITEMS.filter((item) => !MANAGED_DIRS.includes(item));
|
|
86
86
|
for (const item of managedFiles) {
|
|
87
87
|
const dest = join(profileDir, item);
|
|
@@ -90,7 +90,7 @@ export const createCommand = (name, options) => {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// COPY_DIRS → empty directories (commands, plugins, workflows, scripts, …)
|
|
93
|
-
// These must exist so
|
|
93
|
+
// These must exist so restoreFiles can copy profile directories into ~/.claude
|
|
94
94
|
for (const dir of COPY_DIRS) {
|
|
95
95
|
mkdirSync(join(profileDir, dir), { recursive: true });
|
|
96
96
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { createInterface } from 'node:readline';
|
|
7
|
+
import { success, info, warn, error } from '../output-helpers.js';
|
|
8
|
+
|
|
9
|
+
const METHODS = ['npm', 'brew', 'standalone'];
|
|
10
|
+
const STANDALONE_INSTALL_URL = 'https://raw.githubusercontent.com/ThanhThi2895/claude-switch-profile/main/install.sh';
|
|
11
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(modulePath);
|
|
13
|
+
const INSTALL_SCRIPT = join(__dirname, '..', '..', 'install.sh');
|
|
14
|
+
const TEST_DRY_RUN = process.env.NODE_ENV === 'test' || process.env.CSP_UPDATE_DRY_RUN === '1';
|
|
15
|
+
|
|
16
|
+
const confirm = (question) => new Promise((resolve) => {
|
|
17
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
18
|
+
rl.question(question, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
resolve(answer.toLowerCase().startsWith('y'));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const normalizeMethod = (method) => {
|
|
25
|
+
const normalized = (method || '').toLowerCase();
|
|
26
|
+
if (!normalized) return null;
|
|
27
|
+
return METHODS.includes(normalized) ? normalized : null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const detectInstallMethod = () => {
|
|
31
|
+
const normalizedPath = modulePath.replaceAll('\\', '/').toLowerCase();
|
|
32
|
+
const normalizedHome = homedir().replaceAll('\\', '/').toLowerCase();
|
|
33
|
+
|
|
34
|
+
if (normalizedPath.includes(`${normalizedHome}/.csp-cli/`)) return 'standalone';
|
|
35
|
+
if (normalizedPath.includes('/cellar/claude-switch-profile/')) return 'brew';
|
|
36
|
+
return 'npm';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const runCommand = ({ command, args, label }) => {
|
|
40
|
+
const rendered = [command, ...args].join(' ');
|
|
41
|
+
|
|
42
|
+
if (TEST_DRY_RUN) {
|
|
43
|
+
info(`${label} (dry run in test mode):`);
|
|
44
|
+
info(` ${rendered}`);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const result = spawnSync(command, args, { stdio: 'inherit' });
|
|
49
|
+
|
|
50
|
+
if (result.error) {
|
|
51
|
+
error(`${label} failed: ${result.error.message}`);
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (result.status !== 0) {
|
|
57
|
+
error(`${label} failed.`);
|
|
58
|
+
process.exitCode = result.status || 1;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const runStandaloneUpdate = () => {
|
|
66
|
+
if (existsSync(INSTALL_SCRIPT)) {
|
|
67
|
+
return runCommand({ command: 'bash', args: [INSTALL_SCRIPT], label: 'Standalone update' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return runCommand({
|
|
71
|
+
command: 'bash',
|
|
72
|
+
args: ['-lc', `curl -fsSL ${STANDALONE_INSTALL_URL} | bash`],
|
|
73
|
+
label: 'Standalone update',
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const updateCommand = async (options = {}) => {
|
|
78
|
+
const hasExplicitMethod = typeof options.method === 'string' && options.method.length > 0;
|
|
79
|
+
const method = hasExplicitMethod ? normalizeMethod(options.method) : detectInstallMethod();
|
|
80
|
+
|
|
81
|
+
if (hasExplicitMethod && !method) {
|
|
82
|
+
warn('Missing or invalid --method. Use one of: npm, brew, standalone');
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('');
|
|
88
|
+
info('This updates the csp CLI only.');
|
|
89
|
+
info('Profiles are kept at ~/.claude-profiles (no data is removed).');
|
|
90
|
+
info(`Method: ${method}${hasExplicitMethod ? '' : ' (auto-detected; override with --method)'}`);
|
|
91
|
+
console.log('');
|
|
92
|
+
|
|
93
|
+
if (!options.force) {
|
|
94
|
+
const confirmed = await confirm(`Proceed update for method "${method}"? (y/N) `);
|
|
95
|
+
if (!confirmed) {
|
|
96
|
+
warn('Cancelled.');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (method === 'standalone') {
|
|
102
|
+
if (runStandaloneUpdate()) success('csp update completed.');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (method === 'brew') {
|
|
107
|
+
if (runCommand({ command: 'brew', args: ['upgrade', 'claude-switch-profile'], label: 'Homebrew update' })) {
|
|
108
|
+
success('csp update completed.');
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (runCommand({ command: 'npm', args: ['install', '-g', 'claude-switch-profile@latest'], label: 'npm update' })) {
|
|
114
|
+
success('csp update completed.');
|
|
115
|
+
}
|
|
116
|
+
};
|
package/src/commands/use.js
CHANGED
|
@@ -7,15 +7,8 @@ import {
|
|
|
7
7
|
setPrevious,
|
|
8
8
|
ensureDefaultProfileSnapshot,
|
|
9
9
|
} from '../profile-store.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
saveFiles,
|
|
13
|
-
removeFiles,
|
|
14
|
-
restoreFiles,
|
|
15
|
-
updateSettingsPaths,
|
|
16
|
-
moveDirsToProfile,
|
|
17
|
-
moveDirsToClaude,
|
|
18
|
-
} from '../file-operations.js';
|
|
10
|
+
import { copyItems, restoreItems, removeItems } from '../item-manager.js';
|
|
11
|
+
import { saveFiles, removeFiles, restoreFiles, updateSettingsPaths } from '../file-operations.js';
|
|
19
12
|
import { validateProfile } from '../profile-validator.js';
|
|
20
13
|
import { withLock, assertClaudeNotRunning } from '../safety.js';
|
|
21
14
|
import { success, error, info, warn } from '../output-helpers.js';
|
|
@@ -94,9 +87,8 @@ export const useCommand = async (name, options = {}) => {
|
|
|
94
87
|
await withLock(async () => {
|
|
95
88
|
if (active && profileExists(active) && options.save !== false) {
|
|
96
89
|
const activeDir = getProfileDir(active);
|
|
97
|
-
|
|
90
|
+
copyItems(activeDir);
|
|
98
91
|
saveFiles(activeDir);
|
|
99
|
-
moveDirsToProfile(activeDir);
|
|
100
92
|
updateSettingsPaths(activeDir, 'save');
|
|
101
93
|
info(`Saved current state to "${active}"`);
|
|
102
94
|
}
|
|
@@ -105,9 +97,8 @@ export const useCommand = async (name, options = {}) => {
|
|
|
105
97
|
removeFiles();
|
|
106
98
|
|
|
107
99
|
try {
|
|
108
|
-
|
|
100
|
+
restoreItems(profileDir);
|
|
109
101
|
restoreFiles(profileDir);
|
|
110
|
-
moveDirsToClaude(profileDir);
|
|
111
102
|
updateSettingsPaths(CLAUDE_DIR, 'restore', profileDir);
|
|
112
103
|
} catch (err) {
|
|
113
104
|
warn(`Switch failed: ${err.message}`);
|
package/src/constants.js
CHANGED
|
@@ -19,7 +19,7 @@ export const RUNTIMES_DIR = join(PROFILES_DIR, RUNTIME_DIR_NAME);
|
|
|
19
19
|
export const LAUNCH_CONFIG_ENV = 'CLAUDE_CONFIG_DIR';
|
|
20
20
|
export const LAUNCH_ANTHROPIC_ENV_KEYS = ['ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'ANTHROPIC_MODEL'];
|
|
21
21
|
|
|
22
|
-
// Items managed by snapshot/
|
|
22
|
+
// Items managed by snapshot/copy flows; file/dir/symlink shape is preserved
|
|
23
23
|
export const MANAGED_ITEMS = [
|
|
24
24
|
'CLAUDE.md',
|
|
25
25
|
'rules',
|
package/src/file-operations.js
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
import { existsSync, cpSync, unlinkSync, rmSync, mkdirSync, readFileSync, writeFileSync
|
|
1
|
+
import { existsSync, cpSync, unlinkSync, rmSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { CLAUDE_DIR, COPY_ITEMS, COPY_DIRS } from './constants.js';
|
|
4
4
|
|
|
5
|
-
// Rename-based dir move with EXDEV fallback
|
|
6
|
-
const moveDir = (src, dest) => {
|
|
7
|
-
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
8
|
-
try {
|
|
9
|
-
renameSync(src, dest);
|
|
10
|
-
} catch (err) {
|
|
11
|
-
if (err.code === 'EXDEV') {
|
|
12
|
-
cpSync(src, dest, { recursive: true, verbatimSymlinks: true });
|
|
13
|
-
rmSync(src, { recursive: true, force: true });
|
|
14
|
-
} else throw err;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
5
|
const copyPathPreservingSymlink = (src, dest) => {
|
|
19
6
|
mkdirSync(dirname(dest), { recursive: true });
|
|
20
7
|
rmSync(dest, { recursive: true, force: true });
|
|
@@ -78,28 +65,6 @@ export const removeFiles = () => {
|
|
|
78
65
|
}
|
|
79
66
|
};
|
|
80
67
|
|
|
81
|
-
// Move COPY_DIRS from ~/.claude → profileDir (destructive, for use command)
|
|
82
|
-
// COPY_ITEMS always copied (tiny files, not worth move complexity)
|
|
83
|
-
export const moveDirsToProfile = (profileDir) => {
|
|
84
|
-
mkdirSync(profileDir, { recursive: true });
|
|
85
|
-
for (const dir of COPY_DIRS) {
|
|
86
|
-
const src = join(CLAUDE_DIR, dir);
|
|
87
|
-
if (existsSync(src)) {
|
|
88
|
-
moveDir(src, join(profileDir, dir));
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// Move COPY_DIRS from profileDir → ~/.claude (destructive, for use command)
|
|
94
|
-
export const moveDirsToClaude = (profileDir) => {
|
|
95
|
-
for (const dir of COPY_DIRS) {
|
|
96
|
-
const src = join(profileDir, dir);
|
|
97
|
-
if (existsSync(src)) {
|
|
98
|
-
moveDir(src, join(CLAUDE_DIR, dir));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
68
|
/**
|
|
104
69
|
* Update absolute paths in settings.json.
|
|
105
70
|
* Direction: 'save' replaces CLAUDE_DIR → profileDir, 'restore' replaces profileDir → CLAUDE_DIR.
|
package/src/item-manager.js
CHANGED
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, cpSync, rmSync, mkdirSync, lstatSync
|
|
2
|
-
import { join, dirname
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, cpSync, rmSync, mkdirSync, lstatSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
3
|
import { CLAUDE_DIR, MANAGED_ITEMS, SOURCE_FILE } from './constants.js';
|
|
4
4
|
|
|
5
|
-
const SKIP_PATTERNS = ['.venv', 'node_modules', '__pycache__', '.git'];
|
|
6
|
-
const skipHeavyDirs = (src) => !SKIP_PATTERNS.includes(basename(src));
|
|
7
|
-
|
|
8
|
-
// Rename-based move with EXDEV fallback (filtered copy + delete)
|
|
9
|
-
const moveItem = (src, dest) => {
|
|
10
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
11
|
-
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
12
|
-
try {
|
|
13
|
-
renameSync(src, dest);
|
|
14
|
-
} catch (err) {
|
|
15
|
-
if (err.code === 'EXDEV') {
|
|
16
|
-
cpSync(src, dest, { recursive: true, filter: skipHeavyDirs, verbatimSymlinks: true });
|
|
17
|
-
rmSync(src, { recursive: true, force: true });
|
|
18
|
-
} else throw err;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
5
|
|
|
22
6
|
// Read current managed items from ~/.claude — returns map of {item: claudeDir/item}
|
|
23
7
|
export const readCurrentItems = () => {
|
|
@@ -88,6 +72,8 @@ export const restoreItems = (profileDir) => {
|
|
|
88
72
|
if (!MANAGED_ITEMS.includes(item)) continue;
|
|
89
73
|
|
|
90
74
|
const dest = join(CLAUDE_DIR, item);
|
|
75
|
+
const localSrc = join(profileDir, item);
|
|
76
|
+
const restoreSrc = existsSync(localSrc) ? localSrc : srcPath;
|
|
91
77
|
|
|
92
78
|
try {
|
|
93
79
|
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
@@ -95,10 +81,10 @@ export const restoreItems = (profileDir) => {
|
|
|
95
81
|
// fine
|
|
96
82
|
}
|
|
97
83
|
|
|
98
|
-
//
|
|
99
|
-
if (existsSync(
|
|
84
|
+
// Prefer profile-local snapshot; fallback to legacy external source path
|
|
85
|
+
if (restoreSrc && existsSync(restoreSrc)) {
|
|
100
86
|
try {
|
|
101
|
-
copyItemPreservingSymlink(
|
|
87
|
+
copyItemPreservingSymlink(restoreSrc, dest);
|
|
102
88
|
} catch {
|
|
103
89
|
// skip unreadable
|
|
104
90
|
}
|
|
@@ -108,42 +94,3 @@ export const restoreItems = (profileDir) => {
|
|
|
108
94
|
return sourceMap;
|
|
109
95
|
};
|
|
110
96
|
|
|
111
|
-
// Move items from ~/.claude → profileDir (destructive — items leave ~/.claude)
|
|
112
|
-
export const moveItemsToProfile = (profileDir) => {
|
|
113
|
-
mkdirSync(profileDir, { recursive: true });
|
|
114
|
-
const sourceMap = {};
|
|
115
|
-
for (const item of MANAGED_ITEMS) {
|
|
116
|
-
const itemPath = join(CLAUDE_DIR, item);
|
|
117
|
-
if (!existsSync(itemPath)) continue;
|
|
118
|
-
try {
|
|
119
|
-
const dest = join(profileDir, item);
|
|
120
|
-
moveItem(itemPath, dest);
|
|
121
|
-
sourceMap[item] = dest;
|
|
122
|
-
} catch {
|
|
123
|
-
// skip
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
writeFileSync(join(profileDir, SOURCE_FILE), JSON.stringify(sourceMap, null, 2) + '\n');
|
|
127
|
-
return sourceMap;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// Move items from profileDir → ~/.claude (destructive — items leave profileDir)
|
|
131
|
-
export const moveItemsToClaude = (profileDir) => {
|
|
132
|
-
const sourcePath = join(profileDir, SOURCE_FILE);
|
|
133
|
-
if (!existsSync(sourcePath)) return {};
|
|
134
|
-
const sourceMap = JSON.parse(readFileSync(sourcePath, 'utf-8'));
|
|
135
|
-
|
|
136
|
-
for (const [item] of Object.entries(sourceMap)) {
|
|
137
|
-
if (!MANAGED_ITEMS.includes(item)) continue;
|
|
138
|
-
const src = join(profileDir, item);
|
|
139
|
-
const dest = join(CLAUDE_DIR, item);
|
|
140
|
-
if (!existsSync(src)) continue;
|
|
141
|
-
try {
|
|
142
|
-
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
143
|
-
moveItem(src, dest);
|
|
144
|
-
} catch {
|
|
145
|
-
// skip
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return sourceMap;
|
|
149
|
-
};
|