claude-switch-profile 1.4.0 → 1.4.2
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/.cocoindex_code/cocoindex.db/mdb/data.mdb +0 -0
- package/.cocoindex_code/cocoindex.db/mdb/lock.mdb +0 -0
- package/.cocoindex_code/settings.yml +41 -0
- package/.cocoindex_code/target_sqlite.db +0 -0
- package/CHANGELOG.md +19 -0
- package/README.md +112 -50
- package/package.json +1 -1
- package/src/commands/create.js +9 -26
- package/src/commands/current.js +13 -2
- package/src/commands/deactivate.js +6 -8
- package/src/commands/diff.js +6 -4
- package/src/commands/export.js +7 -4
- package/src/commands/import.js +47 -2
- package/src/commands/init.js +29 -9
- package/src/commands/launch.js +10 -5
- package/src/commands/save.js +8 -4
- package/src/commands/uninstall.js +19 -22
- package/src/commands/use.js +55 -50
- package/src/constants.js +1 -1
- package/src/file-operations.js +13 -11
- package/src/item-manager.js +10 -16
- package/src/profile-store.js +37 -14
- package/src/runtime-instance-manager.js +29 -29
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
exclude_patterns:
|
|
2
|
+
- '**/.*'
|
|
3
|
+
- '**/__pycache__'
|
|
4
|
+
- '**/node_modules'
|
|
5
|
+
- '**/target'
|
|
6
|
+
- '**/build/assets'
|
|
7
|
+
- '**/dist'
|
|
8
|
+
- '**/vendor/*.*/*'
|
|
9
|
+
- '**/vendor/*'
|
|
10
|
+
- '**/.cocoindex_code'
|
|
11
|
+
include_patterns:
|
|
12
|
+
- '**/*.py'
|
|
13
|
+
- '**/*.pyi'
|
|
14
|
+
- '**/*.js'
|
|
15
|
+
- '**/*.jsx'
|
|
16
|
+
- '**/*.ts'
|
|
17
|
+
- '**/*.tsx'
|
|
18
|
+
- '**/*.mjs'
|
|
19
|
+
- '**/*.cjs'
|
|
20
|
+
- '**/*.rs'
|
|
21
|
+
- '**/*.go'
|
|
22
|
+
- '**/*.java'
|
|
23
|
+
- '**/*.c'
|
|
24
|
+
- '**/*.h'
|
|
25
|
+
- '**/*.cpp'
|
|
26
|
+
- '**/*.hpp'
|
|
27
|
+
- '**/*.cc'
|
|
28
|
+
- '**/*.cxx'
|
|
29
|
+
- '**/*.hxx'
|
|
30
|
+
- '**/*.hh'
|
|
31
|
+
- '**/*.cs'
|
|
32
|
+
- '**/*.sql'
|
|
33
|
+
- '**/*.sh'
|
|
34
|
+
- '**/*.bash'
|
|
35
|
+
- '**/*.zsh'
|
|
36
|
+
- '**/*.md'
|
|
37
|
+
- '**/*.mdx'
|
|
38
|
+
- '**/*.txt'
|
|
39
|
+
- '**/*.rst'
|
|
40
|
+
- '**/*.php'
|
|
41
|
+
- '**/*.lua'
|
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `claude-switch-profile` are documented here.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- _No changes yet._
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## [1.4.1] - 2026-03-31
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- `default` is now a physical profile snapshot created by `csp init` instead of a virtual pass-through.
|
|
16
|
+
- `csp save`, `csp use`, `csp export`, `csp launch`, and `csp uninstall` now operate on the `default` snapshot the same way they do for other profiles.
|
|
17
|
+
- Legacy installs missing `~/.claude-profiles/default` only backfill that snapshot when the active profile is `default` or no active profile is set; if a non-default profile is active, CSP fails closed with repair guidance.
|
|
18
|
+
- Protected and session/runtime files remain excluded from snapshot capture, export, isolated runtime sync, and restore flows.
|
|
19
|
+
- Launching `default` keeps its metadata mode as `legacy` instead of drifting to `account-session`.
|
|
20
|
+
- README command reference refreshed to match current CLI behavior (`select` default command, `status`, `toggle`, `create --source`, import symlink safety validation, updated project structure, and GitHub repository link).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
5
24
|
## [1.3.0] - 2026-03-28
|
|
6
25
|
|
|
7
26
|
### Added
|
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
[](https://nodejs.org)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
+
**GitHub:** https://github.com/ThanhThi2895/claude-switch-profile
|
|
8
|
+
|
|
7
9
|
A CLI tool for managing multiple Claude Code configurations and profiles. Use legacy global switching (`csp use`) or concurrent isolated account sessions (`csp launch`) with per-profile runtime roots.
|
|
8
10
|
|
|
9
11
|
## Overview
|
|
@@ -53,13 +55,13 @@ csp --help
|
|
|
53
55
|
|
|
54
56
|
### 1. Initialize
|
|
55
57
|
|
|
56
|
-
Capture your current Claude Code setup as the default profile:
|
|
58
|
+
Capture your current Claude Code setup as the physical `default` profile snapshot:
|
|
57
59
|
|
|
58
60
|
```bash
|
|
59
61
|
csp init
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
This creates `~/.claude-profiles/default/`
|
|
64
|
+
This creates `~/.claude-profiles/default/` from the current managed contents of `~/.claude`. Protected and session/runtime files remain excluded.
|
|
63
65
|
|
|
64
66
|
### 2. Create Additional Profiles
|
|
65
67
|
|
|
@@ -116,7 +118,15 @@ Output example:
|
|
|
116
118
|
|
|
117
119
|
The `*` marks the active profile.
|
|
118
120
|
|
|
119
|
-
### 6.
|
|
121
|
+
### 6. Launch via Interactive Selector (Default Command)
|
|
122
|
+
|
|
123
|
+
Run `csp` without a subcommand to open the interactive selector and launch a profile immediately:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
csp
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 7. Uninstall CSP
|
|
120
130
|
|
|
121
131
|
Remove CSP and restore your Claude Code to its original state:
|
|
122
132
|
|
|
@@ -127,6 +137,22 @@ csp uninstall
|
|
|
127
137
|
|
|
128
138
|
## Commands Reference
|
|
129
139
|
|
|
140
|
+
### select (default)
|
|
141
|
+
|
|
142
|
+
Open interactive profile selector (default behavior when you run `csp` without subcommand).
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
csp
|
|
146
|
+
csp select
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Behavior:**
|
|
150
|
+
- If TTY and multiple profiles exist: shows arrow-key menu and launches selected profile via `csp launch <name>`
|
|
151
|
+
- If only one profile exists: shows informational message and exits
|
|
152
|
+
- If non-interactive terminal: asks to use `csp launch <name>` or `csp use <name>` directly
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
130
156
|
### init
|
|
131
157
|
|
|
132
158
|
Initialize the profile system and capture current state as "default" profile.
|
|
@@ -182,6 +208,16 @@ The `*` marks the currently active profile.
|
|
|
182
208
|
|
|
183
209
|
---
|
|
184
210
|
|
|
211
|
+
### status
|
|
212
|
+
|
|
213
|
+
Show CSP status dashboard (active profile, profile count, last launch, Claude process state).
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
csp status
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
185
221
|
### create
|
|
186
222
|
|
|
187
223
|
Create a new profile from current state or clone existing profile.
|
|
@@ -192,6 +228,7 @@ csp create <name> [options]
|
|
|
192
228
|
|
|
193
229
|
**Options:**
|
|
194
230
|
- `--from <profile>` — Clone from existing profile instead of current state
|
|
231
|
+
- `-s, --source <path>` — Create from a specific kit directory, then inherit missing managed items from current `~/.claude`
|
|
195
232
|
- `-d, --description <text>` — Add description to profile
|
|
196
233
|
|
|
197
234
|
**Examples:**
|
|
@@ -211,10 +248,16 @@ Clone from existing:
|
|
|
211
248
|
csp create backup --from production
|
|
212
249
|
```
|
|
213
250
|
|
|
251
|
+
Create from kit directory:
|
|
252
|
+
```bash
|
|
253
|
+
csp create team-kit --source ~/my-kit/.agents -d "Team baseline"
|
|
254
|
+
```
|
|
255
|
+
|
|
214
256
|
**Behavior:**
|
|
215
257
|
- Creates profile directory: `~/.claude-profiles/<name>/`
|
|
216
258
|
- If `--from` is specified, clones all content from source profile
|
|
217
|
-
-
|
|
259
|
+
- If `--source` is specified, copies managed items from that directory, then fills missing managed items from current `~/.claude`
|
|
260
|
+
- Otherwise, performs full clone of current `~/.claude` excluding protected/session items
|
|
218
261
|
- If this is the first profile, automatically sets it as active
|
|
219
262
|
- Saves metadata (created timestamp, description)
|
|
220
263
|
|
|
@@ -229,10 +272,11 @@ csp save
|
|
|
229
272
|
```
|
|
230
273
|
|
|
231
274
|
**Behavior:**
|
|
232
|
-
- Captures all managed items and files from `~/.claude`
|
|
233
|
-
- Overwrites profile
|
|
234
|
-
-
|
|
235
|
-
-
|
|
275
|
+
- Captures all managed items and copied files/directories from `~/.claude`
|
|
276
|
+
- Overwrites the active profile snapshot (`source.json` plus copied content)
|
|
277
|
+
- When `default` is active, updates the physical `default` snapshot like any other profile
|
|
278
|
+
- Protected and session/runtime files remain excluded
|
|
279
|
+
- Requires an active profile
|
|
236
280
|
|
|
237
281
|
---
|
|
238
282
|
|
|
@@ -247,7 +291,6 @@ csp use <name> [options]
|
|
|
247
291
|
**Options:**
|
|
248
292
|
- `--dry-run` — Show what would change without executing
|
|
249
293
|
- `--no-save` — Skip saving current profile before switching
|
|
250
|
-
- `--force` — Accepted for compatibility (current switch path does not block on target-link validation)
|
|
251
294
|
|
|
252
295
|
**Examples:**
|
|
253
296
|
|
|
@@ -266,19 +309,30 @@ Switch without saving current state:
|
|
|
266
309
|
csp use backup --no-save
|
|
267
310
|
```
|
|
268
311
|
|
|
269
|
-
Force switch with missing targets:
|
|
270
|
-
```bash
|
|
271
|
-
csp use legacy --force
|
|
272
|
-
```
|
|
273
|
-
|
|
274
312
|
**Behavior:**
|
|
275
313
|
1. Validates target profile exists and profile structure is valid
|
|
276
314
|
2. Refuses to switch while Claude Code is running (legacy/global switching mutates `~/.claude` directly)
|
|
277
|
-
3. If active profile exists and `--no-save` is not set: saves current
|
|
278
|
-
4. Removes managed items/files from `~/.claude`
|
|
279
|
-
5. Restores target profile
|
|
315
|
+
3. If the active profile exists and `--no-save` is not set: saves its current snapshot first
|
|
316
|
+
4. Removes managed items/files from `~/.claude`
|
|
317
|
+
5. Restores the target profile snapshot into `~/.claude` — including `default`
|
|
280
318
|
6. Updates active marker
|
|
281
|
-
7.
|
|
319
|
+
7. On older installs missing `profiles/default`, CSP only backfills that snapshot when the active profile is `default` or no active profile is set; otherwise it fails closed with repair guidance
|
|
320
|
+
8. **Important:** Claude Code session must be restarted for changes to apply
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### toggle
|
|
325
|
+
|
|
326
|
+
Launch the previous profile (does not mutate global active profile in isolated mode).
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
csp toggle
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Behavior:**
|
|
333
|
+
- Reads previous profile marker from `~/.claude-profiles/.previous`
|
|
334
|
+
- Validates previous profile still exists
|
|
335
|
+
- Delegates to `csp launch <previous>`
|
|
282
336
|
|
|
283
337
|
---
|
|
284
338
|
|
|
@@ -308,10 +362,11 @@ csp delete old-setup --force
|
|
|
308
362
|
```
|
|
309
363
|
|
|
310
364
|
**Behavior:**
|
|
311
|
-
- Cannot delete
|
|
365
|
+
- Cannot delete `default`
|
|
312
366
|
- Prompts for confirmation unless `--force` is used
|
|
313
367
|
- Permanently removes profile directory and metadata
|
|
314
|
-
-
|
|
368
|
+
- If deleting the currently active non-default profile: clears active marker only (does not mutate `~/.claude`)
|
|
369
|
+
- Cannot be undone
|
|
315
370
|
|
|
316
371
|
---
|
|
317
372
|
|
|
@@ -340,8 +395,10 @@ csp export staging -o ~/backups/claude-staging.tar.gz
|
|
|
340
395
|
```
|
|
341
396
|
|
|
342
397
|
**Behavior:**
|
|
343
|
-
- Creates tar.gz archive of
|
|
344
|
-
- Includes `source.json`
|
|
398
|
+
- Creates tar.gz archive of the profile snapshot directory
|
|
399
|
+
- Includes `source.json` plus copied profile files/directories
|
|
400
|
+
- Exporting `default` works like any other profile snapshot
|
|
401
|
+
- Protected and session/runtime files remain excluded from the archive
|
|
345
402
|
- Useful for backup, sharing, or version control
|
|
346
403
|
|
|
347
404
|
---
|
|
@@ -374,6 +431,8 @@ csp import backup.tar.gz -n restored -d "Restored from backup"
|
|
|
374
431
|
**Behavior:**
|
|
375
432
|
- Extracts archive to `~/.claude-profiles/<name>/`
|
|
376
433
|
- Uses filename as profile name if `--name` not specified
|
|
434
|
+
- Validates imported profile structure
|
|
435
|
+
- Rejects import if managed/copied items contain symlink targets outside profile directory (safety check)
|
|
377
436
|
- Creates metadata entry for profile
|
|
378
437
|
- Profile is ready to use immediately
|
|
379
438
|
|
|
@@ -422,7 +481,7 @@ File differences:
|
|
|
422
481
|
|
|
423
482
|
### deactivate
|
|
424
483
|
|
|
425
|
-
Deactivate the currently active non-default profile
|
|
484
|
+
Deactivate the currently active non-default profile by switching back to the physical `default` snapshot.
|
|
426
485
|
|
|
427
486
|
```bash
|
|
428
487
|
csp deactivate
|
|
@@ -433,9 +492,10 @@ csp deactivate
|
|
|
433
492
|
|
|
434
493
|
**Behavior:**
|
|
435
494
|
1. Exits early if no active profile or active profile is `default`
|
|
436
|
-
2.
|
|
437
|
-
3.
|
|
438
|
-
4.
|
|
495
|
+
2. Delegates to `csp use default`
|
|
496
|
+
3. Optionally saves the current non-default profile state
|
|
497
|
+
4. Restores the physical `default` snapshot into `~/.claude`
|
|
498
|
+
5. Marks `default` as the active legacy profile
|
|
439
499
|
|
|
440
500
|
---
|
|
441
501
|
|
|
@@ -471,20 +531,22 @@ csp launch work --legacy-global
|
|
|
471
531
|
|
|
472
532
|
**Behavior (default isolated mode):**
|
|
473
533
|
1. Validates target profile exists
|
|
474
|
-
2.
|
|
475
|
-
3.
|
|
476
|
-
4.
|
|
477
|
-
5.
|
|
478
|
-
6.
|
|
479
|
-
7.
|
|
480
|
-
8.
|
|
534
|
+
2. Ensures the profile snapshot exists; for legacy installs missing `default/`, guarded backfill only runs when the active profile is `default` or no active profile is set
|
|
535
|
+
3. Prepares per-profile runtime under `~/.claude-profiles/.runtime/<name>`
|
|
536
|
+
4. Resolves effective allowlisted `ANTHROPIC_*` launch env (`ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_MODEL`) with precedence: `settings.json env` > profile `.env` allowlist > parent process env
|
|
537
|
+
5. Strips inherited `CLAUDECODE`, inherited `CLAUDE_CONFIG_DIR`, inherited `ANTHROPIC_*`, and Claude session env vars before applying resolved allowlisted values
|
|
538
|
+
6. Spawns `claude` with `CLAUDE_CONFIG_DIR=<runtimeDir>`
|
|
539
|
+
7. Inherits stdin/stdout/stderr for interactive use
|
|
540
|
+
8. Forwards Claude's exit code
|
|
541
|
+
9. Keeps `.active` unchanged and never mutates global `~/.claude`
|
|
542
|
+
10. Launching `default` preserves its `legacy` mode metadata even though it uses an isolated runtime snapshot
|
|
481
543
|
|
|
482
544
|
`ANTHROPIC_*` keys currently in isolated launch scope:
|
|
483
545
|
- `ANTHROPIC_AUTH_TOKEN`
|
|
484
546
|
- `ANTHROPIC_BASE_URL`
|
|
485
547
|
- `ANTHROPIC_MODEL`
|
|
486
548
|
|
|
487
|
-
Set `CSP_DEBUG_LAUNCH_ENV=1` to print
|
|
549
|
+
Set `CSP_DEBUG_LAUNCH_ENV=1` to print extended launch diagnostics. Do not use in shared logs because it can include resolved `ANTHROPIC_*` values.
|
|
488
550
|
|
|
489
551
|
---
|
|
490
552
|
|
|
@@ -520,9 +582,10 @@ csp uninstall --force
|
|
|
520
582
|
|
|
521
583
|
**Behavior:**
|
|
522
584
|
1. Creates a final backup at `~/.claude-profiles/.backup/`
|
|
523
|
-
2. Restores the active profile
|
|
524
|
-
3.
|
|
525
|
-
4.
|
|
585
|
+
2. Restores the active profile by default, or the `--profile` choice, to `~/.claude`
|
|
586
|
+
3. If `default` is restored, it uses the physical `default` snapshot like any other profile
|
|
587
|
+
4. Removes `~/.claude-profiles/` entirely
|
|
588
|
+
5. Prints reminder to run `npm uninstall -g claude-switch-profile`
|
|
526
589
|
|
|
527
590
|
---
|
|
528
591
|
|
|
@@ -561,7 +624,7 @@ Profiles are stored in `~/.claude-profiles/`:
|
|
|
561
624
|
- `agents/` — Agent scripts and configurations
|
|
562
625
|
- `skills/` — Custom Luna skills
|
|
563
626
|
- `hooks/` — Pre/post action hooks
|
|
564
|
-
- `statusline.cjs` — Custom statusline
|
|
627
|
+
- `statusline.cjs`, `statusline.sh`, `statusline.ps1` — Custom statusline scripts
|
|
565
628
|
- `.luna.json` — Luna configuration
|
|
566
629
|
|
|
567
630
|
**Copied Files**:
|
|
@@ -583,7 +646,7 @@ Profiles are stored in `~/.claude-profiles/`:
|
|
|
583
646
|
- `history.jsonl`, `metadata.json`, `stats-cache.json`, `active-plan`
|
|
584
647
|
- `backups/`, `command-archive/`, `commands-archived/`
|
|
585
648
|
- `plans/`, `todos/`, `tasks/`, `teams/`, `agent-memory/`, `file-history/`, `shell-snapshots/`
|
|
586
|
-
- All other session-specific data
|
|
649
|
+
- All other protected or session-specific data
|
|
587
650
|
|
|
588
651
|
### Legacy vs Isolated Launch Modes
|
|
589
652
|
|
|
@@ -664,11 +727,7 @@ Before switching, CSP validates:
|
|
|
664
727
|
1. Target profile exists
|
|
665
728
|
2. Profile structure is valid
|
|
666
729
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
```bash
|
|
670
|
-
csp use legacy --force
|
|
671
|
-
```
|
|
730
|
+
During import, CSP also validates profile contents and rejects unsafe symlinks that point outside the imported profile tree.
|
|
672
731
|
|
|
673
732
|
## Configuration via Environment Variables
|
|
674
733
|
|
|
@@ -727,7 +786,7 @@ csp use personal # Switch to personal
|
|
|
727
786
|
|
|
728
787
|
```bash
|
|
729
788
|
# Clone current profile
|
|
730
|
-
csp create experimental --from
|
|
789
|
+
csp create experimental --from default -d "Testing new Luna skills"
|
|
731
790
|
|
|
732
791
|
# Switch and experiment
|
|
733
792
|
csp use experimental
|
|
@@ -791,15 +850,14 @@ csp list
|
|
|
791
850
|
csp use production # if "production" exists
|
|
792
851
|
```
|
|
793
852
|
|
|
794
|
-
### Cannot delete
|
|
853
|
+
### Cannot delete default profile
|
|
795
854
|
|
|
796
|
-
**Error:** `Cannot delete
|
|
855
|
+
**Error:** `Cannot delete the default profile.`
|
|
797
856
|
|
|
798
|
-
**Solution:**
|
|
857
|
+
**Solution:** Keep `default` and delete only non-default profiles.
|
|
799
858
|
|
|
800
859
|
```bash
|
|
801
|
-
csp
|
|
802
|
-
csp delete default
|
|
860
|
+
csp delete experimental
|
|
803
861
|
```
|
|
804
862
|
|
|
805
863
|
### Stale lock file
|
|
@@ -855,13 +913,16 @@ See [CHANGELOG.md](CHANGELOG.md) for version history and migration guidance.
|
|
|
855
913
|
│ │ ├── init.js
|
|
856
914
|
│ │ ├── current.js
|
|
857
915
|
│ │ ├── list.js
|
|
916
|
+
│ │ ├── status.js
|
|
858
917
|
│ │ ├── create.js
|
|
859
918
|
│ │ ├── save.js
|
|
860
919
|
│ │ ├── use.js
|
|
920
|
+
│ │ ├── toggle.js
|
|
861
921
|
│ │ ├── delete.js
|
|
862
922
|
│ │ ├── export.js
|
|
863
923
|
│ │ ├── import.js
|
|
864
924
|
│ │ ├── diff.js
|
|
925
|
+
│ │ ├── select.js
|
|
865
926
|
│ │ ├── launch.js
|
|
866
927
|
│ │ ├── deactivate.js
|
|
867
928
|
│ │ └── uninstall.js
|
|
@@ -871,6 +932,7 @@ See [CHANGELOG.md](CHANGELOG.md) for version history and migration guidance.
|
|
|
871
932
|
│ ├── runtime-instance-manager.js # Isolated runtime sync
|
|
872
933
|
│ ├── item-manager.js # Managed item copy/move operations
|
|
873
934
|
│ ├── file-operations.js # File copy/restore operations
|
|
935
|
+
│ ├── launch-effective-env-resolver.js # ANTHROPIC_* launch env resolution
|
|
874
936
|
│ ├── safety.js # Locking, backups, validation
|
|
875
937
|
│ ├── profile-validator.js # Profile validation
|
|
876
938
|
│ └── output-helpers.js # Console output formatting
|
package/package.json
CHANGED
package/src/commands/create.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, cpSync, existsSync,
|
|
1
|
+
import { mkdirSync, cpSync, existsSync, writeFileSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { addProfile, getActive, setActive, profileExists, getProfileDir } from '../profile-store.js';
|
|
4
4
|
import { saveFiles, updateSettingsPaths } from '../file-operations.js';
|
|
@@ -20,7 +20,7 @@ export const createCommand = (name, options) => {
|
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
const sourceDir = getProfileDir(options.from);
|
|
23
|
-
cpSync(sourceDir, profileDir, { recursive: true });
|
|
23
|
+
cpSync(sourceDir, profileDir, { recursive: true, verbatimSymlinks: true });
|
|
24
24
|
info(`Cloned from profile "${options.from}"`);
|
|
25
25
|
} else if (options.source) {
|
|
26
26
|
// Create from a specific .agents/ directory (or any kit directory)
|
|
@@ -39,11 +39,7 @@ export const createCommand = (name, options) => {
|
|
|
39
39
|
if (existsSync(target)) {
|
|
40
40
|
const dest = join(profileDir, item);
|
|
41
41
|
try {
|
|
42
|
-
|
|
43
|
-
cpSync(target, dest, { recursive: true });
|
|
44
|
-
} else {
|
|
45
|
-
copyFileSync(target, dest);
|
|
46
|
-
}
|
|
42
|
+
cpSync(target, dest, { recursive: true, verbatimSymlinks: true });
|
|
47
43
|
sourceMap[item] = dest;
|
|
48
44
|
} catch { /* skip */ }
|
|
49
45
|
}
|
|
@@ -60,11 +56,7 @@ export const createCommand = (name, options) => {
|
|
|
60
56
|
if (!existsSync(src)) continue;
|
|
61
57
|
try {
|
|
62
58
|
const dest = join(profileDir, item);
|
|
63
|
-
|
|
64
|
-
cpSync(src, dest, { recursive: true });
|
|
65
|
-
} else {
|
|
66
|
-
copyFileSync(src, dest);
|
|
67
|
-
}
|
|
59
|
+
cpSync(src, dest, { recursive: true, verbatimSymlinks: true });
|
|
68
60
|
sourceMap[item] = dest;
|
|
69
61
|
} catch { /* skip */ }
|
|
70
62
|
}
|
|
@@ -92,20 +84,11 @@ export const createCommand = (name, options) => {
|
|
|
92
84
|
|
|
93
85
|
const src = join(CLAUDE_DIR, entry);
|
|
94
86
|
try {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (MANAGED_ITEMS.includes(entry)) {
|
|
101
|
-
sourceMap[entry] = dest;
|
|
102
|
-
}
|
|
103
|
-
} else if (stat.isFile()) {
|
|
104
|
-
const dest = join(profileDir, entry);
|
|
105
|
-
copyFileSync(src, dest);
|
|
106
|
-
if (MANAGED_ITEMS.includes(entry)) {
|
|
107
|
-
sourceMap[entry] = dest;
|
|
108
|
-
}
|
|
87
|
+
const dest = join(profileDir, entry);
|
|
88
|
+
cpSync(src, dest, { recursive: true, verbatimSymlinks: true });
|
|
89
|
+
|
|
90
|
+
if (MANAGED_ITEMS.includes(entry)) {
|
|
91
|
+
sourceMap[entry] = dest;
|
|
109
92
|
}
|
|
110
93
|
} catch { /* skip unreadable items */ }
|
|
111
94
|
}
|
package/src/commands/current.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { getActive, getProfileDir, getProfileMeta } from '../profile-store.js';
|
|
2
|
-
import {
|
|
1
|
+
import { getActive, getProfileDir, getProfileMeta, ensureDefaultProfileSnapshot } from '../profile-store.js';
|
|
2
|
+
import { DEFAULT_PROFILE } from '../constants.js';
|
|
3
|
+
import { success, info, warn, error } from '../output-helpers.js';
|
|
3
4
|
|
|
4
5
|
export const currentCommand = () => {
|
|
5
6
|
const active = getActive();
|
|
@@ -7,6 +8,16 @@ export const currentCommand = () => {
|
|
|
7
8
|
warn('No active profile. Run "csp create <name>" to create one.');
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
11
|
+
|
|
12
|
+
if (active === DEFAULT_PROFILE) {
|
|
13
|
+
try {
|
|
14
|
+
ensureDefaultProfileSnapshot();
|
|
15
|
+
} catch (err) {
|
|
16
|
+
error(err.message);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
success(`Active legacy profile: ${active}`);
|
|
11
22
|
info(`Location: ${getProfileDir(active)}`);
|
|
12
23
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { getActive
|
|
1
|
+
import { getActive } from '../profile-store.js';
|
|
2
2
|
import { success, info } from '../output-helpers.js';
|
|
3
3
|
import { DEFAULT_PROFILE } from '../constants.js';
|
|
4
|
+
import { useCommand } from './use.js';
|
|
4
5
|
|
|
5
|
-
export const deactivateCommand = async () => {
|
|
6
|
+
export const deactivateCommand = async (options = {}) => {
|
|
6
7
|
const active = getActive();
|
|
7
8
|
if (!active) {
|
|
8
9
|
info('No active profile to deactivate.');
|
|
@@ -10,13 +11,10 @@ export const deactivateCommand = async () => {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (active === DEFAULT_PROFILE) {
|
|
13
|
-
info('Default profile
|
|
14
|
+
info('Default profile is already active.');
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
success(`Profile "${active}" deactivated. Reset to default.`);
|
|
21
|
-
info('~/.claude was not modified.');
|
|
18
|
+
await useCommand(DEFAULT_PROFILE, options);
|
|
19
|
+
success(`Profile "${active}" deactivated.`);
|
|
22
20
|
};
|
package/src/commands/diff.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { profileExists, getActive, getEffectiveDir } from '../profile-store.js';
|
|
4
|
+
import { profileExists, getActive, getEffectiveDir, getProfileDir } from '../profile-store.js';
|
|
5
5
|
import { SOURCE_FILE, ALL_MANAGED } from '../constants.js';
|
|
6
6
|
import { error, info } from '../output-helpers.js';
|
|
7
7
|
|
|
@@ -33,12 +33,14 @@ export const diffCommand = (profileA, profileB) => {
|
|
|
33
33
|
|
|
34
34
|
const dirA = getEffectiveDir(nameA);
|
|
35
35
|
const dirB = getEffectiveDir(nameB);
|
|
36
|
+
const sourceDirA = join(getProfileDir(nameA), SOURCE_FILE);
|
|
37
|
+
const sourceDirB = join(getProfileDir(nameB), SOURCE_FILE);
|
|
36
38
|
|
|
37
39
|
console.log(`\n${chalk.bold('Comparing:')} ${chalk.cyan(nameA)} ↔ ${chalk.cyan(nameB)}\n`);
|
|
38
40
|
|
|
39
|
-
// Compare source.json
|
|
40
|
-
const sourceA = readJsonSafe(
|
|
41
|
-
const sourceB = readJsonSafe(
|
|
41
|
+
// Compare source.json from stored profile dirs, not live ~/.claude state
|
|
42
|
+
const sourceA = readJsonSafe(sourceDirA);
|
|
43
|
+
const sourceB = readJsonSafe(sourceDirB);
|
|
42
44
|
diffObject('Managed item sources (source.json)', sourceA, sourceB, nameA, nameB);
|
|
43
45
|
|
|
44
46
|
// Compare files that exist in either profile
|
package/src/commands/export.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
import { profileExists, getProfileDir, getActive } from '../profile-store.js';
|
|
3
|
+
import { profileExists, getProfileDir, getActive, ensureDefaultProfileSnapshot } from '../profile-store.js';
|
|
4
4
|
import { isWindows } from '../platform.js';
|
|
5
5
|
import { saveItems } from '../item-manager.js';
|
|
6
6
|
import { saveFiles, updateSettingsPaths } from '../file-operations.js';
|
|
@@ -9,8 +9,12 @@ import { DEFAULT_PROFILE } from '../constants.js';
|
|
|
9
9
|
|
|
10
10
|
export const exportCommand = (name, options) => {
|
|
11
11
|
if (name === DEFAULT_PROFILE) {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
try {
|
|
13
|
+
ensureDefaultProfileSnapshot();
|
|
14
|
+
} catch (err) {
|
|
15
|
+
error(err.message);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
if (!profileExists(name)) {
|
|
@@ -22,7 +26,6 @@ export const exportCommand = (name, options) => {
|
|
|
22
26
|
const output = options.output || `./${name}.csp.tar.gz`;
|
|
23
27
|
const outputPath = resolve(output);
|
|
24
28
|
|
|
25
|
-
// If exporting active profile, save a copy first (non-destructive)
|
|
26
29
|
const active = getActive();
|
|
27
30
|
if (active === name) {
|
|
28
31
|
saveItems(profileDir);
|