@deepdiver_sj/claude-settings 0.1.0

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.
@@ -0,0 +1,318 @@
1
+ /**
2
+ * @file src/core/paths.ts
3
+ * @description Claude Settings OS별 경로 계산 핵심 로직
4
+ * @author Claude
5
+ *
6
+ * 기능:
7
+ * - Windows: %APPDATA%, %LOCALAPPDATA% 처리
8
+ * - Linux: XDG Base Directory 표준 준수
9
+ * - macOS: ~/Library 구조 지원
10
+ * - 경로 정규화 및 검증
11
+ */
12
+ import * as path from 'path';
13
+ import * as os from 'os';
14
+ /**
15
+ * 경로 문자열 정규화
16
+ *
17
+ * 기능:
18
+ * 1. OS별로 경로 구분자 통일
19
+ * 2. 더블 슬래시 제거
20
+ * 3. 끝 슬래시 제거
21
+ * 4. 틸드(~) 확장
22
+ *
23
+ * @param {string} inputPath - 입력 경로
24
+ * @param {PlatformType} platform - 운영체제 타입
25
+ * @returns {string} 정규화된 경로
26
+ *
27
+ * @example
28
+ * normalizePath('C:/Users/user//.config/', 'win32')
29
+ * // => 'C:\\Users\\user\\.config'
30
+ */
31
+ function normalizePath(inputPath, platform) {
32
+ // 1️⃣ 틸드 확장
33
+ let normalizedPath = inputPath;
34
+ if (normalizedPath.startsWith('~')) {
35
+ const homeDir = os.homedir();
36
+ normalizedPath = normalizedPath.replace(/^~/, homeDir);
37
+ }
38
+ // 2️⃣ OS별 구분자로 통일
39
+ if (platform === 'win32') {
40
+ // Windows: 모든 슬래시를 백슬래시로 변환
41
+ normalizedPath = normalizedPath.replace(/\//g, '\\');
42
+ }
43
+ else {
44
+ // Linux, macOS: 모든 백슬래시를 슬래시로 변환
45
+ normalizedPath = normalizedPath.replace(/\\/g, '/');
46
+ }
47
+ // 3️⃣ 더블 슬래시 제거 (Windows UNC 경로 제외)
48
+ if (platform === 'win32') {
49
+ // Windows UNC 경로 (\\server\share) 보존
50
+ if (!normalizedPath.startsWith('\\\\')) {
51
+ normalizedPath = normalizedPath.replace(/\\\\/g, '\\');
52
+ }
53
+ else {
54
+ // UNC 경로 내부의 더블 슬래시만 제거
55
+ const unc = normalizedPath.substring(0, 2);
56
+ const rest = normalizedPath.substring(2).replace(/\\\\/g, '\\');
57
+ normalizedPath = unc + rest;
58
+ }
59
+ }
60
+ else {
61
+ // Unix: 더블 슬래시 제거 (단, 루트 경로 제외)
62
+ if (normalizedPath !== '/') {
63
+ normalizedPath = normalizedPath.replace(/\/+/g, '/');
64
+ }
65
+ }
66
+ // 4️⃣ 끝의 슬래시 제거
67
+ if (normalizedPath.length > 1) {
68
+ const sep = platform === 'win32' ? '\\' : '/';
69
+ normalizedPath = normalizedPath.replace(new RegExp(`${sep}+$`), '');
70
+ }
71
+ return normalizedPath;
72
+ }
73
+ /**
74
+ * Windows 경로 계산
75
+ *
76
+ * 우선순위:
77
+ * 1. %APPDATA% (설정 + 데이터)
78
+ * 2. %LOCALAPPDATA% (캐시)
79
+ * 3. %USERPROFILE% (폴백)
80
+ *
81
+ * @param {Partial<PathsConfig>} [customConfig] - 커스텀 설정
82
+ * @returns {PathsConfig} Windows 경로 설정
83
+ * @throws {Error} APPDATA 환경변수가 없을 때
84
+ *
85
+ * @example
86
+ * const paths = getWindowsPaths();
87
+ * // => {
88
+ * // config: 'C:\\Users\\User\\AppData\\Roaming\\claude',
89
+ * // data: 'C:\\Users\\User\\AppData\\Roaming\\claude\\data',
90
+ * // cache: 'C:\\Users\\User\\AppData\\Local\\claude\\cache'
91
+ * // }
92
+ */
93
+ function getWindowsPaths(customConfig) {
94
+ // 환경변수 확인
95
+ const appData = process.env.APPDATA;
96
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
97
+ const userProfile = process.env.USERPROFILE;
98
+ if (!appData && !userProfile) {
99
+ throw new Error('APPDATA environment variable not set. Please ensure you are running on Windows.');
100
+ }
101
+ // 기본값 설정
102
+ const baseAppData = appData || path.join(userProfile || '', 'AppData', 'Roaming');
103
+ const baseLocalAppData = localAppData || path.join(userProfile || '', 'AppData', 'Local');
104
+ return {
105
+ config: customConfig?.config || path.join(baseAppData, 'claude'),
106
+ data: customConfig?.data || path.join(baseAppData, 'claude', 'data'),
107
+ cache: customConfig?.cache || path.join(baseLocalAppData, 'claude', 'cache'),
108
+ backup: customConfig?.backup || path.join(baseAppData, 'claude', 'backup'),
109
+ state: customConfig?.state || path.join(baseAppData, 'claude', 'state'),
110
+ };
111
+ }
112
+ /**
113
+ * Linux 경로 계산 (XDG Base Directory 표준)
114
+ *
115
+ * XDG 환경변수:
116
+ * - $XDG_CONFIG_HOME (~/.config)
117
+ * - $XDG_DATA_HOME (~/.local/share)
118
+ * - $XDG_CACHE_HOME (~/.cache)
119
+ * - $XDG_RUNTIME_DIR (/run/user/{UID})
120
+ *
121
+ * @param {Partial<PathsConfig>} [customConfig] - 커스텀 설정
122
+ * @returns {PathsConfig} Linux 경로 설정
123
+ * @throws {Error} HOME 환경변수가 없을 때
124
+ *
125
+ * @example
126
+ * const paths = getLinuxPaths();
127
+ * // => {
128
+ * // config: '/home/user/.config/claude',
129
+ * // data: '/home/user/.local/share/claude',
130
+ * // cache: '/home/user/.cache/claude',
131
+ * // runtime: '/run/user/1000/claude'
132
+ * // }
133
+ */
134
+ function getLinuxPaths(customConfig) {
135
+ // 홈 디렉토리 확인
136
+ const homeDir = process.env.HOME || os.homedir();
137
+ if (!homeDir) {
138
+ throw new Error('HOME environment variable not set');
139
+ }
140
+ // XDG Base Directory 환경변수 (기본값 설정)
141
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(homeDir, '.config');
142
+ const xdgDataHome = process.env.XDG_DATA_HOME || path.join(homeDir, '.local', 'share');
143
+ const xdgCacheHome = process.env.XDG_CACHE_HOME || path.join(homeDir, '.cache');
144
+ const xdgRuntimeDir = process.env.XDG_RUNTIME_DIR;
145
+ return {
146
+ config: customConfig?.config || path.join(xdgConfigHome, 'claude'),
147
+ data: customConfig?.data || path.join(xdgDataHome, 'claude'),
148
+ cache: customConfig?.cache || path.join(xdgCacheHome, 'claude'),
149
+ backup: customConfig?.backup || path.join(xdgDataHome, 'claude', 'backup'),
150
+ state: customConfig?.state || path.join(xdgDataHome, 'claude', 'state'),
151
+ runtime: customConfig?.runtime || (xdgRuntimeDir ? path.join(xdgRuntimeDir, 'claude') : undefined),
152
+ };
153
+ }
154
+ /**
155
+ * macOS 경로 계산
156
+ *
157
+ * macOS는 Linux와 달리 XDG 표준을 사용하지 않음.
158
+ * 대신 ~/Library 아래 Apple 가이드라인을 따름:
159
+ *
160
+ * - ~/Library/Preferences: 설정
161
+ * - ~/Library/Application Support: 데이터
162
+ * - ~/Library/Caches: 캐시
163
+ *
164
+ * @param {Partial<PathsConfig>} [customConfig] - 커스텀 설정
165
+ * @returns {PathsConfig} macOS 경로 설정
166
+ *
167
+ * @example
168
+ * const paths = getMacOSPaths();
169
+ * // => {
170
+ * // config: '/Users/user/Library/Preferences/claude',
171
+ * // data: '/Users/user/Library/Application Support/claude',
172
+ * // cache: '/Users/user/Library/Caches/claude'
173
+ * // }
174
+ */
175
+ function getMacOSPaths(customConfig) {
176
+ const homeDir = os.homedir();
177
+ return {
178
+ config: customConfig?.config || path.join(homeDir, 'Library', 'Preferences', 'claude'),
179
+ data: customConfig?.data || path.join(homeDir, 'Library', 'Application Support', 'claude'),
180
+ cache: customConfig?.cache || path.join(homeDir, 'Library', 'Caches', 'claude'),
181
+ backup: customConfig?.backup || path.join(homeDir, 'Library', 'Application Support', 'claude', 'backup'),
182
+ state: customConfig?.state || path.join(homeDir, 'Library', 'Application Support', 'claude', 'state'),
183
+ };
184
+ }
185
+ /**
186
+ * OS별 경로 계산 (메인 함수)
187
+ *
188
+ * @param {PlatformType} [platform] - 운영체제 타입 (기본값: process.platform)
189
+ * @param {Partial<PathsConfig>} [customConfig] - 커스텀 설정
190
+ * @returns {PathsResult} OS별 정규화된 경로
191
+ * @throws {Error} 필수 환경변수가 없거나 지원하지 않는 OS일 때
192
+ *
193
+ * @example
194
+ * // 현재 OS의 경로 획득
195
+ * const paths = getPaths();
196
+ * console.log(paths.config);
197
+ *
198
+ * @example
199
+ * // 특정 OS의 경로 획득
200
+ * const winPaths = getPaths('win32');
201
+ * const linuxPaths = getPaths('linux');
202
+ *
203
+ * @example
204
+ * // 커스텀 경로 적용
205
+ * const customPaths = getPaths('linux', {
206
+ * cache: '/mnt/fast-ssd/claude-cache'
207
+ * });
208
+ */
209
+ export function getPaths(platform = process.platform, customConfig) {
210
+ let pathsConfig;
211
+ // 플랫폼별 경로 계산
212
+ switch (platform) {
213
+ case 'win32':
214
+ pathsConfig = getWindowsPaths(customConfig);
215
+ break;
216
+ case 'linux':
217
+ pathsConfig = getLinuxPaths(customConfig);
218
+ break;
219
+ case 'darwin':
220
+ pathsConfig = getMacOSPaths(customConfig);
221
+ break;
222
+ default:
223
+ throw new Error(`Unsupported platform: ${platform}. Supported platforms: win32, linux, darwin`);
224
+ }
225
+ // 모든 경로 정규화
226
+ const normalizedPaths = {
227
+ config: normalizePath(pathsConfig.config, platform),
228
+ data: normalizePath(pathsConfig.data, platform),
229
+ cache: normalizePath(pathsConfig.cache, platform),
230
+ };
231
+ // 선택 경로 정규화
232
+ if (pathsConfig.backup) {
233
+ normalizedPaths.backup = normalizePath(pathsConfig.backup, platform);
234
+ }
235
+ if (pathsConfig.state) {
236
+ normalizedPaths.state = normalizePath(pathsConfig.state, platform);
237
+ }
238
+ if (pathsConfig.runtime) {
239
+ normalizedPaths.runtime = normalizePath(pathsConfig.runtime, platform);
240
+ }
241
+ return normalizedPaths;
242
+ }
243
+ /**
244
+ * 현재 환경의 경로 빠르게 획득
245
+ *
246
+ * getPaths()의 래퍼 함수로, 플랫폼과 커스텀 설정 없이 기본값만 사용합니다.
247
+ *
248
+ * @param {Partial<PathsConfig>} [customConfig] - 커스텀 설정
249
+ * @returns {PathsResult} 현재 환경의 경로
250
+ *
251
+ * @example
252
+ * const paths = getDefaultPaths();
253
+ */
254
+ export function getDefaultPaths(customConfig) {
255
+ return getPaths(process.platform, customConfig);
256
+ }
257
+ /**
258
+ * 경로 검증
259
+ *
260
+ * @param {string} pathStr - 검증할 경로
261
+ * @param {PlatformType} platform - 운영체제 타입
262
+ * @returns {boolean} 유효한 경로일 경우 true
263
+ *
264
+ * 검증 항목:
265
+ * - 절대 경로인지 확인
266
+ * - OS별 금지 문자 없는지 확인
267
+ *
268
+ * @example
269
+ * isValidPath('C:\\Users\\test\\.config', 'win32') // => true
270
+ * isValidPath('/home/user/.config', 'linux') // => true
271
+ */
272
+ export function isValidPath(pathStr, platform) {
273
+ // 절대 경로 확인
274
+ if (!path.isAbsolute(pathStr)) {
275
+ return false;
276
+ }
277
+ // OS별 금지 문자 확인
278
+ if (platform === 'win32') {
279
+ // Windows 금지 문자: < > : " | ? *
280
+ const invalidCharsRegex = /[<>:"|?*]/;
281
+ if (invalidCharsRegex.test(pathStr)) {
282
+ return false;
283
+ }
284
+ }
285
+ return true;
286
+ }
287
+ /**
288
+ * 경로의 부모 폴더 확장
289
+ *
290
+ * @param {string} pathStr - 입력 경로
291
+ * @param {number} [levels=1] - 올라갈 폴더 레벨
292
+ * @returns {string} 부모 폴더 경로
293
+ *
294
+ * @example
295
+ * getParentPath('/home/user/.config/claude/data', 2)
296
+ * // => '/home/user/.config/claude'
297
+ */
298
+ export function getParentPath(pathStr, levels = 1) {
299
+ let result = pathStr;
300
+ for (let i = 0; i < levels; i++) {
301
+ result = path.dirname(result);
302
+ }
303
+ return result;
304
+ }
305
+ /**
306
+ * Returns the actual Claude Code home directory (same on all platforms).
307
+ * Claude Code always uses ~/.claude regardless of OS.
308
+ */
309
+ export function getClaudeHomePath() {
310
+ return path.join(os.homedir(), '.claude');
311
+ }
312
+ export default {
313
+ getPaths,
314
+ getDefaultPaths,
315
+ isValidPath,
316
+ getParentPath,
317
+ };
318
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ExportOptions, ExportResult } from './types.js';
2
+ /**
3
+ * Replace absolute ~/.claude paths with the portable placeholder.
4
+ *
5
+ * Handles Windows (C:\Users\foo\.claude\)
6
+ * Linux (/home/foo/.claude/)
7
+ * macOS (/Users/foo/.claude/)
8
+ */
9
+ export declare function normalizeClaudePaths(text: string): string;
10
+ /**
11
+ * Export the current ~/.claude environment to a portable snapshot JSON.
12
+ */
13
+ export declare function exportSnapshot(options?: ExportOptions): Promise<ExportResult>;
14
+ //# sourceMappingURL=exporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exporter.d.ts","sourceRoot":"","sources":["../../src/snapshot/exporter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,aAAa,EACb,YAAY,EAGb,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUzD;AA4ED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA+EvB"}
@@ -0,0 +1,168 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { SNAPSHOT } from '../core/constants.js';
4
+ import { getClaudeHomePath } from '../core/paths.js';
5
+ /**
6
+ * Replace absolute ~/.claude paths with the portable placeholder.
7
+ *
8
+ * Handles Windows (C:\Users\foo\.claude\)
9
+ * Linux (/home/foo/.claude/)
10
+ * macOS (/Users/foo/.claude/)
11
+ */
12
+ export function normalizeClaudePaths(text) {
13
+ const placeholder = SNAPSHOT.CLAUDE_HOME_PLACEHOLDER + '/';
14
+ // Windows absolute path: any drive letter + path ending in .claude
15
+ const winPattern = /[A-Za-z]:\\[^"'\n]*?\.claude[\\\/]/g;
16
+ // POSIX absolute path
17
+ const posixPattern = /(?:\/home\/[^\/\n]+|\/Users\/[^\/\n]+)\/\.claude\//g;
18
+ return text
19
+ .replace(winPattern, () => placeholder)
20
+ .replace(posixPattern, () => placeholder);
21
+ }
22
+ /**
23
+ * Recursively collect files under `dirPath`.
24
+ * Paths stored relative to `baseDir`.
25
+ */
26
+ function collectFiles(dirPath, baseDir, files) {
27
+ if (!fs.existsSync(dirPath))
28
+ return;
29
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
30
+ for (const entry of entries) {
31
+ const fullPath = path.join(dirPath, entry.name);
32
+ const relPath = path
33
+ .relative(baseDir, fullPath)
34
+ .replace(/\\/g, '/'); // normalise to forward slashes
35
+ if (entry.isDirectory()) {
36
+ // Skip excluded sub-dirs
37
+ const relDir = path
38
+ .relative(baseDir, fullPath)
39
+ .replace(/\\/g, '/');
40
+ if (SNAPSHOT.EXCLUDE_DIRS.some((d) => relDir === d || relDir.startsWith(d + '/'))) {
41
+ continue;
42
+ }
43
+ collectFiles(fullPath, baseDir, files);
44
+ }
45
+ else if (entry.isFile()) {
46
+ // Skip excluded files
47
+ if (SNAPSHOT.EXCLUDE_FILES.includes(relPath))
48
+ continue;
49
+ try {
50
+ const content = fs.readFileSync(fullPath, 'utf8');
51
+ files.push({ relativePath: relPath, content });
52
+ }
53
+ catch {
54
+ // Binary or unreadable file — skip
55
+ }
56
+ }
57
+ }
58
+ }
59
+ /**
60
+ * Read plugin install manifests from plugins/.install-manifests/.
61
+ * Each manifest is expected to be a JSON file with { name, source, installCmd }.
62
+ */
63
+ function collectPlugins(claudeHome) {
64
+ const plugins = [];
65
+ for (const metaDir of SNAPSHOT.INSTALL_META_DIRS) {
66
+ const manifestsDir = path.join(claudeHome, metaDir);
67
+ if (!fs.existsSync(manifestsDir))
68
+ continue;
69
+ const files = fs.readdirSync(manifestsDir);
70
+ for (const file of files) {
71
+ if (!file.endsWith('.json'))
72
+ continue;
73
+ try {
74
+ const raw = fs.readFileSync(path.join(manifestsDir, file), 'utf8');
75
+ const manifest = JSON.parse(raw);
76
+ if (manifest.name && manifest.source && manifest.installCmd) {
77
+ plugins.push({
78
+ name: manifest.name,
79
+ source: manifest.source,
80
+ installCmd: manifest.installCmd,
81
+ });
82
+ }
83
+ }
84
+ catch {
85
+ // Malformed manifest — skip
86
+ }
87
+ }
88
+ }
89
+ return plugins;
90
+ }
91
+ /**
92
+ * Export the current ~/.claude environment to a portable snapshot JSON.
93
+ */
94
+ export async function exportSnapshot(options = {}) {
95
+ try {
96
+ const claudeHome = getClaudeHomePath();
97
+ if (!fs.existsSync(claudeHome)) {
98
+ return {
99
+ success: false,
100
+ message: `Claude home directory not found: ${claudeHome}`,
101
+ };
102
+ }
103
+ const files = [];
104
+ // 1. Collect top-level include files
105
+ for (const fileName of SNAPSHOT.INCLUDE_FILES) {
106
+ const fullPath = path.join(claudeHome, fileName);
107
+ if (!fs.existsSync(fullPath))
108
+ continue;
109
+ try {
110
+ const content = fs.readFileSync(fullPath, 'utf8');
111
+ files.push({ relativePath: fileName, content });
112
+ }
113
+ catch {
114
+ // skip
115
+ }
116
+ }
117
+ // 2. Collect include directories recursively
118
+ for (const dir of SNAPSHOT.INCLUDE_DIRS) {
119
+ collectFiles(path.join(claudeHome, dir), claudeHome, files);
120
+ }
121
+ // 3. Normalise paths in all collected file contents
122
+ const normalizedFiles = files.map((f) => ({
123
+ ...f,
124
+ content: normalizeClaudePaths(f.content),
125
+ }));
126
+ // 4. Read & normalise settings.json separately (already included above,
127
+ // but we also store it as a parsed object for easy import-time manipulation)
128
+ let settings = {};
129
+ const settingsPath = path.join(claudeHome, 'settings.json');
130
+ if (fs.existsSync(settingsPath)) {
131
+ try {
132
+ const raw = fs.readFileSync(settingsPath, 'utf8');
133
+ const normalised = normalizeClaudePaths(raw);
134
+ settings = JSON.parse(normalised);
135
+ }
136
+ catch {
137
+ // keep empty object
138
+ }
139
+ }
140
+ // 5. Collect plugin manifests
141
+ const plugins = collectPlugins(claudeHome);
142
+ const snapshot = {
143
+ version: SNAPSHOT.SNAPSHOT_VERSION,
144
+ exportedAt: new Date().toISOString(),
145
+ sourceOS: process.platform,
146
+ files: normalizedFiles,
147
+ settings,
148
+ plugins,
149
+ };
150
+ const outputPath = options.outputPath ?? path.join(process.cwd(), 'env-snapshot.json');
151
+ fs.writeFileSync(outputPath, JSON.stringify(snapshot, null, 2), 'utf8');
152
+ return {
153
+ success: true,
154
+ message: `Snapshot exported to ${outputPath}`,
155
+ outputPath,
156
+ fileCount: normalizedFiles.length,
157
+ pluginCount: plugins.length,
158
+ };
159
+ }
160
+ catch (error) {
161
+ return {
162
+ success: false,
163
+ message: `Export failed: ${error.message}`,
164
+ error: error,
165
+ };
166
+ }
167
+ }
168
+ //# sourceMappingURL=exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exporter.js","sourceRoot":"","sources":["../../src/snapshot/exporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ImportOptions, ImportResult } from './types.js';
2
+ /**
3
+ * Replace {{CLAUDE_HOME}} placeholders with the real target path.
4
+ * Uses the OS-native separator so hooks work correctly on the target machine.
5
+ */
6
+ export declare function restoreClaudePaths(text: string, claudeHome: string): string;
7
+ /**
8
+ * Import a snapshot JSON and restore the Claude environment on this machine.
9
+ */
10
+ export declare function importSnapshot(options: ImportOptions): Promise<ImportResult>;
11
+ //# sourceMappingURL=importer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../src/snapshot/importer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAe,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3E;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAwGlF"}
@@ -0,0 +1,118 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { spawnSync } from 'child_process';
4
+ import { SNAPSHOT } from '../core/constants.js';
5
+ import { getClaudeHomePath } from '../core/paths.js';
6
+ /**
7
+ * Replace {{CLAUDE_HOME}} placeholders with the real target path.
8
+ * Uses the OS-native separator so hooks work correctly on the target machine.
9
+ */
10
+ export function restoreClaudePaths(text, claudeHome) {
11
+ // Normalise separator to forward slash for the replacement value
12
+ // because most shell / node scripts accept forward slashes on all platforms
13
+ const normalizedHome = claudeHome.replace(/\\/g, '/');
14
+ const placeholder = SNAPSHOT.CLAUDE_HOME_PLACEHOLDER;
15
+ return text.replaceAll(placeholder, normalizedHome);
16
+ }
17
+ /**
18
+ * Import a snapshot JSON and restore the Claude environment on this machine.
19
+ */
20
+ export async function importSnapshot(options) {
21
+ const errors = [];
22
+ let filesWritten = 0;
23
+ let pluginsInstalled = 0;
24
+ try {
25
+ if (!fs.existsSync(options.snapshotPath)) {
26
+ return {
27
+ success: false,
28
+ message: `Snapshot file not found: ${options.snapshotPath}`,
29
+ errors: [`File not found: ${options.snapshotPath}`],
30
+ };
31
+ }
32
+ const raw = fs.readFileSync(options.snapshotPath, 'utf8');
33
+ const snapshot = JSON.parse(raw);
34
+ const claudeHome = getClaudeHomePath();
35
+ const dryRun = options.dryRun ?? false;
36
+ if (dryRun) {
37
+ console.log(`[dry-run] Target: ${claudeHome}`);
38
+ }
39
+ // 1. Write environment files
40
+ for (const file of snapshot.files) {
41
+ const targetPath = path.join(claudeHome, file.relativePath);
42
+ const restored = restoreClaudePaths(file.content, claudeHome);
43
+ if (dryRun) {
44
+ console.log(`[dry-run] Would write: ${targetPath}`);
45
+ continue;
46
+ }
47
+ try {
48
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
49
+ fs.writeFileSync(targetPath, restored, 'utf8');
50
+ filesWritten++;
51
+ }
52
+ catch (err) {
53
+ errors.push(`Failed to write ${file.relativePath}: ${err.message}`);
54
+ }
55
+ }
56
+ // 2. Write settings.json (path-restored version)
57
+ if (Object.keys(snapshot.settings).length > 0) {
58
+ const settingsStr = JSON.stringify(snapshot.settings, null, 2);
59
+ const restoredSettings = restoreClaudePaths(settingsStr, claudeHome);
60
+ const settingsTarget = path.join(claudeHome, 'settings.json');
61
+ if (dryRun) {
62
+ console.log(`[dry-run] Would write settings.json to ${settingsTarget}`);
63
+ }
64
+ else {
65
+ try {
66
+ fs.mkdirSync(path.dirname(settingsTarget), { recursive: true });
67
+ fs.writeFileSync(settingsTarget, restoredSettings, 'utf8');
68
+ // Don't double-count settings.json (it's already in files[])
69
+ }
70
+ catch (err) {
71
+ errors.push(`Failed to write settings.json: ${err.message}`);
72
+ }
73
+ }
74
+ }
75
+ // 3. Install plugins
76
+ for (const plugin of snapshot.plugins) {
77
+ const restoredCmd = restoreClaudePaths(plugin.installCmd, claudeHome);
78
+ if (dryRun) {
79
+ console.log(`[dry-run] Would install plugin "${plugin.name}": ${restoredCmd}`);
80
+ continue;
81
+ }
82
+ try {
83
+ const parts = restoredCmd.split(' ');
84
+ const result = spawnSync(parts[0] ?? '', parts.slice(1), {
85
+ stdio: 'inherit',
86
+ shell: true,
87
+ });
88
+ if (result.status !== 0) {
89
+ errors.push(`Plugin "${plugin.name}" install exited with code ${result.status ?? 'unknown'}`);
90
+ }
91
+ else {
92
+ pluginsInstalled++;
93
+ }
94
+ }
95
+ catch (err) {
96
+ errors.push(`Plugin "${plugin.name}" install error: ${err.message}`);
97
+ }
98
+ }
99
+ const success = errors.length === 0;
100
+ return {
101
+ success,
102
+ message: dryRun
103
+ ? `Dry-run complete. Would write ${snapshot.files.length} file(s), install ${snapshot.plugins.length} plugin(s).`
104
+ : `Import complete. Wrote ${filesWritten} file(s), installed ${pluginsInstalled} plugin(s).${errors.length ? ` ${errors.length} error(s).` : ''}`,
105
+ filesWritten,
106
+ pluginsInstalled,
107
+ errors: errors.length > 0 ? errors : undefined,
108
+ };
109
+ }
110
+ catch (error) {
111
+ return {
112
+ success: false,
113
+ message: `Import failed: ${error.message}`,
114
+ errors: [error.message],
115
+ };
116
+ }
117
+ }
118
+ //# sourceMappingURL=importer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importer.js","sourceRoot":"","sources":["../../src/snapshot/importer.ts"],"names":[],"mappings":"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"}