@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.
- package/README.md +60 -0
- package/dist/bin/claude-settings.d.ts +51 -0
- package/dist/bin/claude-settings.d.ts.map +1 -0
- package/dist/bin/claude-settings.js +218 -0
- package/dist/bin/claude-settings.js.map +1 -0
- package/dist/core/constants.d.ts +609 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +463 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/paths.d.ts +134 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +318 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/snapshot/exporter.d.ts +14 -0
- package/dist/snapshot/exporter.d.ts.map +1 -0
- package/dist/snapshot/exporter.js +168 -0
- package/dist/snapshot/exporter.js.map +1 -0
- package/dist/snapshot/importer.d.ts +11 -0
- package/dist/snapshot/importer.d.ts.map +1 -0
- package/dist/snapshot/importer.js +118 -0
- package/dist/snapshot/importer.js.map +1 -0
- package/dist/snapshot/types.d.ts +57 -0
- package/dist/snapshot/types.d.ts.map +1 -0
- package/dist/snapshot/types.js +5 -0
- package/dist/snapshot/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -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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"}
|