claude-switch-profile 1.4.8 → 1.4.11

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/CHANGELOG.md CHANGED
@@ -9,6 +9,36 @@ All notable changes to `claude-switch-profile` are documented here.
9
9
 
10
10
  ---
11
11
 
12
+ ## [1.4.10] - 2026-04-01
13
+
14
+ ### Fixed
15
+ - **Settings Path Mutation Bug**: Fixed a severe issue where hook paths in `settings.json` could exponentially multiply (like `.runtime/default-profiles/.runtime/...`) when saving a profile post-launch. Path mapping logic was strengthened to use exact-boundary Regular Expressions, preventing `.claude` patterns from incorrectly matching adjacent nested paths like `.claude-profiles`.
16
+
17
+ ---
18
+
19
+ ## [1.4.9] - 2026-04-01
20
+
21
+ ### Fixed
22
+ - **Hooks execution in isolated mode**: Fixed an issue where Claude Code hook scripts failed to execute in isolated profiles. The runtime instance manager now correctly resolves shell variables (`$HOME`, `${HOME}`, `~`) within `settings.json` hook commands to accurately point to the isolated runtime directory.
23
+
24
+ ---
25
+
26
+ ## [1.4.8] - 2026-04-01
27
+
28
+ ### Changed
29
+ - Re-ran release flow to publish the latest patch after `1.4.7`.
30
+ - No functional code changes compared with `1.4.7`; this release aligns npm package/version metadata.
31
+
32
+ ---
33
+
34
+ ## [1.4.7] - 2026-04-01
35
+
36
+ ### Fixed
37
+ - Release pipeline now restores annotated git tag publishing.
38
+ - Local agent artifacts are ignored by git to keep release working tree clean.
39
+
40
+ ---
41
+
12
42
  ## [1.4.2] - 2026-03-31
13
43
 
14
44
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-switch-profile",
3
- "version": "1.4.8",
3
+ "version": "1.4.11",
4
4
  "description": "CLI tool for managing multiple Claude Code profiles",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,7 @@ import {
11
11
  readlinkSync,
12
12
  } from 'node:fs';
13
13
  import { join, dirname } from 'node:path';
14
+ import { homedir } from 'node:os';
14
15
  import { MANAGED_ITEMS, COPY_ITEMS, COPY_DIRS, CLAUDE_DIR } from './constants.js';
15
16
  import {
16
17
  getActive,
@@ -76,6 +77,8 @@ const shouldSyncDir = (src, dest) => {
76
77
  }
77
78
  };
78
79
 
80
+ const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
81
+
79
82
  const replacePathVariants = (content, fromPath, toPath) => {
80
83
  const fromEscaped = fromPath.replaceAll('\\', '\\\\');
81
84
  const toEscaped = toPath.replaceAll('\\', '\\\\');
@@ -83,10 +86,20 @@ const replacePathVariants = (content, fromPath, toPath) => {
83
86
  const toFwd = toPath.replaceAll('\\', '/');
84
87
 
85
88
  let updated = content;
86
- updated = updated.replaceAll(fromEscaped, toEscaped);
87
- updated = updated.replaceAll(fromFwd, toFwd);
89
+
90
+ // Prevent matching substrings of other paths (e.g. ~/.claude matching ~/.claude-profiles)
91
+ // by ensuring it's not immediately followed by an alphanumeric character or hyphen.
92
+ const negativeLookahead = '(?![a-zA-Z0-9\\-])';
93
+
94
+ const replaceWithRegex = (str, targetF, targetT) => {
95
+ const re = new RegExp(escapeRegExp(targetF) + negativeLookahead, 'g');
96
+ return str.replace(re, targetT);
97
+ };
98
+
99
+ updated = replaceWithRegex(updated, fromEscaped, toEscaped);
100
+ updated = replaceWithRegex(updated, fromFwd, toFwd);
88
101
  if (fromPath !== fromEscaped) {
89
- updated = updated.replaceAll(fromPath, toPath);
102
+ updated = replaceWithRegex(updated, fromPath, toPath);
90
103
  }
91
104
 
92
105
  return updated;
@@ -103,6 +116,25 @@ const rewriteSettingsForRuntime = (runtimeDir, sourceDir) => {
103
116
  updated = replacePathVariants(updated, sourceDir, runtimeDir);
104
117
  updated = replacePathVariants(updated, CLAUDE_DIR, runtimeDir);
105
118
 
119
+ // Also replace shell variable patterns commonly used in hook commands.
120
+ // Hook commands in settings.json often reference paths like:
121
+ // $HOME/.claude/hooks/... or ${HOME}/.claude/hooks/... or ~/.claude/hooks/...
122
+ // These won't match the literal path replacement above.
123
+ const home = homedir();
124
+ const claudeRelSuffix = CLAUDE_DIR.startsWith(home) ? CLAUDE_DIR.slice(home.length) : '/.claude';
125
+
126
+ const shellPatterns = [
127
+ `$HOME${claudeRelSuffix}`,
128
+ `\${HOME}${claudeRelSuffix}`,
129
+ `~${claudeRelSuffix}`,
130
+ ];
131
+
132
+ for (const pattern of shellPatterns) {
133
+ if (updated.includes(pattern)) {
134
+ updated = updated.replaceAll(pattern, runtimeDir);
135
+ }
136
+ }
137
+
106
138
  if (updated !== raw) {
107
139
  writeFileSync(settingsPath, updated);
108
140
  }