claude-den 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 +101 -0
- package/dist/bin/den.d.ts +2 -0
- package/dist/bin/den.js +1467 -0
- package/dist/bin/den.js.map +1 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.js +775 -0
- package/dist/index.js.map +1 -0
- package/dist/src/index.d.ts +83 -0
- package/dist/src/index.js +777 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +52 -0
package/dist/bin/den.js
ADDED
|
@@ -0,0 +1,1467 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/program.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/cli/init.ts
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
|
+
import chalk2 from "chalk";
|
|
9
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
10
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
11
|
+
import { homedir as homedir3 } from "os";
|
|
12
|
+
|
|
13
|
+
// src/detect/terminal.ts
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
var TERMINAL_ENV_CHECKS = [
|
|
16
|
+
{ env: "GHOSTTY_RESOURCES_DIR", type: "ghostty", name: "Ghostty" },
|
|
17
|
+
{ env: "TERM_PROGRAM", value: "iTerm.app", type: "iterm2", name: "iTerm2" },
|
|
18
|
+
{ env: "TERM_PROGRAM", value: "WarpTerminal", type: "warp", name: "Warp" },
|
|
19
|
+
{ env: "KITTY_PID", type: "kitty", name: "Kitty" },
|
|
20
|
+
{ env: "ALACRITTY_LOG", type: "alacritty", name: "Alacritty" },
|
|
21
|
+
{
|
|
22
|
+
env: "TERM_PROGRAM",
|
|
23
|
+
value: "Apple_Terminal",
|
|
24
|
+
type: "terminal.app",
|
|
25
|
+
name: "Terminal.app"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
env: "WT_SESSION",
|
|
29
|
+
type: "windows-terminal",
|
|
30
|
+
name: "Windows Terminal"
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
var CONFIG_PATHS = {
|
|
34
|
+
ghostty: "~/.config/ghostty/config",
|
|
35
|
+
iterm2: "~/Library/Preferences/com.googlecode.iterm2.plist",
|
|
36
|
+
kitty: "~/.config/kitty/kitty.conf",
|
|
37
|
+
alacritty: "~/.config/alacritty/alacritty.toml",
|
|
38
|
+
warp: "~/.warp/themes",
|
|
39
|
+
"terminal.app": null,
|
|
40
|
+
"windows-terminal": null,
|
|
41
|
+
unknown: null
|
|
42
|
+
};
|
|
43
|
+
function expandHome(filepath) {
|
|
44
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
45
|
+
return filepath.replace(/^~/, home);
|
|
46
|
+
}
|
|
47
|
+
function getVersion(type) {
|
|
48
|
+
const versionEnvMap = {
|
|
49
|
+
iterm2: "TERM_PROGRAM_VERSION"
|
|
50
|
+
};
|
|
51
|
+
const envKey = versionEnvMap[type];
|
|
52
|
+
return envKey ? process.env[envKey] ?? null : null;
|
|
53
|
+
}
|
|
54
|
+
function getConfigPath(type) {
|
|
55
|
+
const template = CONFIG_PATHS[type];
|
|
56
|
+
if (!template) return null;
|
|
57
|
+
const expanded = expandHome(template);
|
|
58
|
+
return expanded;
|
|
59
|
+
}
|
|
60
|
+
function detectTerminal(env = process.env) {
|
|
61
|
+
for (const check2 of TERMINAL_ENV_CHECKS) {
|
|
62
|
+
const envValue = env[check2.env];
|
|
63
|
+
if (envValue === void 0) continue;
|
|
64
|
+
if (check2.value && envValue !== check2.value) continue;
|
|
65
|
+
return {
|
|
66
|
+
type: check2.type,
|
|
67
|
+
name: check2.name,
|
|
68
|
+
version: getVersion(check2.type),
|
|
69
|
+
configPath: getConfigPath(check2.type)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
type: "unknown",
|
|
74
|
+
name: "Unknown Terminal",
|
|
75
|
+
version: null,
|
|
76
|
+
configPath: null
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function isTerminalInstalled(type) {
|
|
80
|
+
const appPaths = {
|
|
81
|
+
ghostty: "/Applications/Ghostty.app",
|
|
82
|
+
iterm2: "/Applications/iTerm.app",
|
|
83
|
+
warp: "/Applications/Warp.app",
|
|
84
|
+
kitty: "/Applications/kitty.app",
|
|
85
|
+
alacritty: "/Applications/Alacritty.app"
|
|
86
|
+
};
|
|
87
|
+
const appPath = appPaths[type];
|
|
88
|
+
if (!appPath) return false;
|
|
89
|
+
return existsSync(appPath);
|
|
90
|
+
}
|
|
91
|
+
function listInstalledTerminals() {
|
|
92
|
+
const terminals = [];
|
|
93
|
+
for (const check2 of TERMINAL_ENV_CHECKS) {
|
|
94
|
+
if (check2.type !== "terminal.app" && check2.type !== "windows-terminal" && check2.type !== "unknown") {
|
|
95
|
+
if (isTerminalInstalled(check2.type)) {
|
|
96
|
+
terminals.push({
|
|
97
|
+
type: check2.type,
|
|
98
|
+
name: check2.name,
|
|
99
|
+
version: null,
|
|
100
|
+
configPath: getConfigPath(check2.type)
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return terminals;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/detect/capabilities.ts
|
|
109
|
+
function detectCapabilities(env = process.env) {
|
|
110
|
+
const colorTerm = env.COLORTERM ?? "";
|
|
111
|
+
const term = env.TERM ?? "";
|
|
112
|
+
const termProgram = env.TERM_PROGRAM ?? "";
|
|
113
|
+
const trueColor = colorTerm === "truecolor" || colorTerm === "24bit" || term.includes("256color") || termProgram === "iTerm.app" || termProgram === "WarpTerminal" || env.GHOSTTY_RESOURCES_DIR !== void 0;
|
|
114
|
+
const color256 = trueColor || term.includes("256color") || term === "xterm";
|
|
115
|
+
const unicode = (() => {
|
|
116
|
+
const lang = env.LANG ?? "";
|
|
117
|
+
const lcAll = env.LC_ALL ?? "";
|
|
118
|
+
return lang.includes("UTF-8") || lang.includes("utf-8") || lcAll.includes("UTF-8") || lcAll.includes("utf-8");
|
|
119
|
+
})();
|
|
120
|
+
const mouseSupport = env.GHOSTTY_RESOURCES_DIR !== void 0 || termProgram === "iTerm.app" || env.KITTY_PID !== void 0;
|
|
121
|
+
const hyperlinks = env.GHOSTTY_RESOURCES_DIR !== void 0 || termProgram === "iTerm.app" || env.KITTY_PID !== void 0 || termProgram === "WarpTerminal";
|
|
122
|
+
const sixel = env.KITTY_PID !== void 0;
|
|
123
|
+
return {
|
|
124
|
+
trueColor,
|
|
125
|
+
color256,
|
|
126
|
+
unicode,
|
|
127
|
+
mouseSupport,
|
|
128
|
+
hyperlinks,
|
|
129
|
+
sixel
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/detect/os.ts
|
|
134
|
+
import { platform, arch, homedir } from "os";
|
|
135
|
+
import { existsSync as existsSync2 } from "fs";
|
|
136
|
+
function detectOS(env = process.env) {
|
|
137
|
+
const osType = resolveOSType(env);
|
|
138
|
+
const shell = resolveShell(env);
|
|
139
|
+
return {
|
|
140
|
+
type: osType,
|
|
141
|
+
arch: arch(),
|
|
142
|
+
shell,
|
|
143
|
+
homeDir: homedir()
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function resolveOSType(env) {
|
|
147
|
+
const p = platform();
|
|
148
|
+
if (p === "darwin") return "macos";
|
|
149
|
+
if (p === "win32") return "windows";
|
|
150
|
+
if (p === "linux") {
|
|
151
|
+
const isWSL = env.WSL_DISTRO_NAME !== void 0 || env.WSLENV !== void 0 || existsSync2("/proc/sys/fs/binfmt_misc/WSLInterop");
|
|
152
|
+
return isWSL ? "wsl" : "linux";
|
|
153
|
+
}
|
|
154
|
+
return "linux";
|
|
155
|
+
}
|
|
156
|
+
function resolveShell(env) {
|
|
157
|
+
const shell = env.SHELL ?? env.ComSpec ?? "";
|
|
158
|
+
if (shell.includes("zsh")) return "zsh";
|
|
159
|
+
if (shell.includes("bash")) return "bash";
|
|
160
|
+
if (shell.includes("fish")) return "fish";
|
|
161
|
+
if (shell.includes("powershell") || shell.includes("pwsh")) return "powershell";
|
|
162
|
+
return shell.split("/").pop() ?? "unknown";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/themes/catppuccin-mocha.ts
|
|
166
|
+
var catppuccinMocha = {
|
|
167
|
+
name: "catppuccin-mocha",
|
|
168
|
+
displayName: "Catppuccin Mocha",
|
|
169
|
+
description: "Soothing pastel theme with warm purple tones",
|
|
170
|
+
author: "Catppuccin",
|
|
171
|
+
variant: "dark",
|
|
172
|
+
colors: {
|
|
173
|
+
background: "#1e1e2e",
|
|
174
|
+
foreground: "#cdd6f4",
|
|
175
|
+
cursor: "#f5e0dc",
|
|
176
|
+
selectionBackground: "#585b70",
|
|
177
|
+
selectionForeground: "#cdd6f4",
|
|
178
|
+
black: "#45475a",
|
|
179
|
+
red: "#f38ba8",
|
|
180
|
+
green: "#a6e3a1",
|
|
181
|
+
yellow: "#f9e2af",
|
|
182
|
+
blue: "#89b4fa",
|
|
183
|
+
magenta: "#f5c2e7",
|
|
184
|
+
cyan: "#94e2d5",
|
|
185
|
+
white: "#bac2de",
|
|
186
|
+
brightBlack: "#585b70",
|
|
187
|
+
brightRed: "#f38ba8",
|
|
188
|
+
brightGreen: "#a6e3a1",
|
|
189
|
+
brightYellow: "#f9e2af",
|
|
190
|
+
brightBlue: "#89b4fa",
|
|
191
|
+
brightMagenta: "#f5c2e7",
|
|
192
|
+
brightCyan: "#94e2d5",
|
|
193
|
+
brightWhite: "#a6adc8"
|
|
194
|
+
},
|
|
195
|
+
appearance: {
|
|
196
|
+
backgroundOpacity: 0.85,
|
|
197
|
+
backgroundBlur: 30,
|
|
198
|
+
cursorStyle: "bar",
|
|
199
|
+
cursorBlink: true
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// src/themes/nord.ts
|
|
204
|
+
var nord = {
|
|
205
|
+
name: "nord",
|
|
206
|
+
displayName: "Nord",
|
|
207
|
+
description: "Arctic, north-bluish color palette",
|
|
208
|
+
author: "Arctic Ice Studio",
|
|
209
|
+
variant: "dark",
|
|
210
|
+
colors: {
|
|
211
|
+
background: "#2e3440",
|
|
212
|
+
foreground: "#d8dee9",
|
|
213
|
+
cursor: "#d8dee9",
|
|
214
|
+
selectionBackground: "#434c5e",
|
|
215
|
+
selectionForeground: "#d8dee9",
|
|
216
|
+
black: "#3b4252",
|
|
217
|
+
red: "#bf616a",
|
|
218
|
+
green: "#a3be8c",
|
|
219
|
+
yellow: "#ebcb8b",
|
|
220
|
+
blue: "#81a1c1",
|
|
221
|
+
magenta: "#b48ead",
|
|
222
|
+
cyan: "#88c0d0",
|
|
223
|
+
white: "#e5e9f0",
|
|
224
|
+
brightBlack: "#4c566a",
|
|
225
|
+
brightRed: "#bf616a",
|
|
226
|
+
brightGreen: "#a3be8c",
|
|
227
|
+
brightYellow: "#ebcb8b",
|
|
228
|
+
brightBlue: "#81a1c1",
|
|
229
|
+
brightMagenta: "#b48ead",
|
|
230
|
+
brightCyan: "#8fbcbb",
|
|
231
|
+
brightWhite: "#eceff4"
|
|
232
|
+
},
|
|
233
|
+
appearance: {
|
|
234
|
+
backgroundOpacity: 0.9,
|
|
235
|
+
backgroundBlur: 20,
|
|
236
|
+
cursorStyle: "bar",
|
|
237
|
+
cursorBlink: true
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// src/themes/tokyo-night.ts
|
|
242
|
+
var tokyoNight = {
|
|
243
|
+
name: "tokyo-night",
|
|
244
|
+
displayName: "Tokyo Night",
|
|
245
|
+
description: "Clean dark theme inspired by Tokyo city lights",
|
|
246
|
+
author: "Enkia",
|
|
247
|
+
variant: "dark",
|
|
248
|
+
colors: {
|
|
249
|
+
background: "#1a1b26",
|
|
250
|
+
foreground: "#c0caf5",
|
|
251
|
+
cursor: "#c0caf5",
|
|
252
|
+
selectionBackground: "#33467c",
|
|
253
|
+
selectionForeground: "#c0caf5",
|
|
254
|
+
black: "#15161e",
|
|
255
|
+
red: "#f7768e",
|
|
256
|
+
green: "#9ece6a",
|
|
257
|
+
yellow: "#e0af68",
|
|
258
|
+
blue: "#7aa2f7",
|
|
259
|
+
magenta: "#bb9af7",
|
|
260
|
+
cyan: "#7dcfff",
|
|
261
|
+
white: "#a9b1d6",
|
|
262
|
+
brightBlack: "#414868",
|
|
263
|
+
brightRed: "#f7768e",
|
|
264
|
+
brightGreen: "#9ece6a",
|
|
265
|
+
brightYellow: "#e0af68",
|
|
266
|
+
brightBlue: "#7aa2f7",
|
|
267
|
+
brightMagenta: "#bb9af7",
|
|
268
|
+
brightCyan: "#7dcfff",
|
|
269
|
+
brightWhite: "#c0caf5"
|
|
270
|
+
},
|
|
271
|
+
appearance: {
|
|
272
|
+
backgroundOpacity: 0.88,
|
|
273
|
+
backgroundBlur: 25,
|
|
274
|
+
cursorStyle: "bar",
|
|
275
|
+
cursorBlink: true
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// src/themes/dracula.ts
|
|
280
|
+
var dracula = {
|
|
281
|
+
name: "dracula",
|
|
282
|
+
displayName: "Dracula",
|
|
283
|
+
description: "Dark theme with vibrant colors",
|
|
284
|
+
author: "Dracula Theme",
|
|
285
|
+
variant: "dark",
|
|
286
|
+
colors: {
|
|
287
|
+
background: "#282a36",
|
|
288
|
+
foreground: "#f8f8f2",
|
|
289
|
+
cursor: "#f8f8f2",
|
|
290
|
+
selectionBackground: "#44475a",
|
|
291
|
+
selectionForeground: "#f8f8f2",
|
|
292
|
+
black: "#21222c",
|
|
293
|
+
red: "#ff5555",
|
|
294
|
+
green: "#50fa7b",
|
|
295
|
+
yellow: "#f1fa8c",
|
|
296
|
+
blue: "#bd93f9",
|
|
297
|
+
magenta: "#ff79c6",
|
|
298
|
+
cyan: "#8be9fd",
|
|
299
|
+
white: "#f8f8f2",
|
|
300
|
+
brightBlack: "#6272a4",
|
|
301
|
+
brightRed: "#ff6e6e",
|
|
302
|
+
brightGreen: "#69ff94",
|
|
303
|
+
brightYellow: "#ffffa5",
|
|
304
|
+
brightBlue: "#d6acff",
|
|
305
|
+
brightMagenta: "#ff92df",
|
|
306
|
+
brightCyan: "#a4ffff",
|
|
307
|
+
brightWhite: "#ffffff"
|
|
308
|
+
},
|
|
309
|
+
appearance: {
|
|
310
|
+
backgroundOpacity: 0.9,
|
|
311
|
+
backgroundBlur: 20,
|
|
312
|
+
cursorStyle: "bar",
|
|
313
|
+
cursorBlink: true
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/themes/gruvbox.ts
|
|
318
|
+
var gruvbox = {
|
|
319
|
+
name: "gruvbox",
|
|
320
|
+
displayName: "Gruvbox Dark",
|
|
321
|
+
description: "Retro groove color scheme with warm earthy tones",
|
|
322
|
+
author: "morhetz",
|
|
323
|
+
variant: "dark",
|
|
324
|
+
colors: {
|
|
325
|
+
background: "#282828",
|
|
326
|
+
foreground: "#ebdbb2",
|
|
327
|
+
cursor: "#ebdbb2",
|
|
328
|
+
selectionBackground: "#504945",
|
|
329
|
+
selectionForeground: "#ebdbb2",
|
|
330
|
+
black: "#282828",
|
|
331
|
+
red: "#cc241d",
|
|
332
|
+
green: "#98971a",
|
|
333
|
+
yellow: "#d79921",
|
|
334
|
+
blue: "#458588",
|
|
335
|
+
magenta: "#b16286",
|
|
336
|
+
cyan: "#689d6a",
|
|
337
|
+
white: "#a89984",
|
|
338
|
+
brightBlack: "#928374",
|
|
339
|
+
brightRed: "#fb4934",
|
|
340
|
+
brightGreen: "#b8bb26",
|
|
341
|
+
brightYellow: "#fabd2f",
|
|
342
|
+
brightBlue: "#83a598",
|
|
343
|
+
brightMagenta: "#d3869b",
|
|
344
|
+
brightCyan: "#8ec07c",
|
|
345
|
+
brightWhite: "#ebdbb2"
|
|
346
|
+
},
|
|
347
|
+
appearance: {
|
|
348
|
+
backgroundOpacity: 0.92,
|
|
349
|
+
backgroundBlur: 15,
|
|
350
|
+
cursorStyle: "block",
|
|
351
|
+
cursorBlink: false
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// src/themes/index.ts
|
|
356
|
+
var THEMES = /* @__PURE__ */ new Map([
|
|
357
|
+
[catppuccinMocha.name, catppuccinMocha],
|
|
358
|
+
[nord.name, nord],
|
|
359
|
+
[tokyoNight.name, tokyoNight],
|
|
360
|
+
[dracula.name, dracula],
|
|
361
|
+
[gruvbox.name, gruvbox]
|
|
362
|
+
]);
|
|
363
|
+
var DEFAULT_THEME = "catppuccin-mocha";
|
|
364
|
+
function getTheme(name) {
|
|
365
|
+
return THEMES.get(name);
|
|
366
|
+
}
|
|
367
|
+
function listThemes() {
|
|
368
|
+
return Array.from(THEMES.values());
|
|
369
|
+
}
|
|
370
|
+
function getThemeOrDefault(name) {
|
|
371
|
+
const theme = name ? THEMES.get(name) : void 0;
|
|
372
|
+
return theme ?? THEMES.get(DEFAULT_THEME);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/configs/ghostty/config.ts
|
|
376
|
+
function generateGhosttyConfig(theme) {
|
|
377
|
+
const { colors, appearance } = theme;
|
|
378
|
+
return `# ============================================
|
|
379
|
+
# Claude Den - Ghostty Config
|
|
380
|
+
# Theme: ${theme.displayName}
|
|
381
|
+
# Generated by claude-den
|
|
382
|
+
# Reload: Cmd + Shift + ,
|
|
383
|
+
# ============================================
|
|
384
|
+
|
|
385
|
+
# --- Typography ---
|
|
386
|
+
font-family = "Maple Mono NF CN"
|
|
387
|
+
font-size = 14
|
|
388
|
+
adjust-cell-height = 2
|
|
389
|
+
|
|
390
|
+
# --- Theme: ${theme.displayName} ---
|
|
391
|
+
background = ${colors.background}
|
|
392
|
+
foreground = ${colors.foreground}
|
|
393
|
+
cursor-color = ${colors.cursor}
|
|
394
|
+
selection-background = ${colors.selectionBackground}
|
|
395
|
+
selection-foreground = ${colors.selectionForeground}
|
|
396
|
+
|
|
397
|
+
palette = 0=${colors.black}
|
|
398
|
+
palette = 1=${colors.red}
|
|
399
|
+
palette = 2=${colors.green}
|
|
400
|
+
palette = 3=${colors.yellow}
|
|
401
|
+
palette = 4=${colors.blue}
|
|
402
|
+
palette = 5=${colors.magenta}
|
|
403
|
+
palette = 6=${colors.cyan}
|
|
404
|
+
palette = 7=${colors.white}
|
|
405
|
+
palette = 8=${colors.brightBlack}
|
|
406
|
+
palette = 9=${colors.brightRed}
|
|
407
|
+
palette = 10=${colors.brightGreen}
|
|
408
|
+
palette = 11=${colors.brightYellow}
|
|
409
|
+
palette = 12=${colors.brightBlue}
|
|
410
|
+
palette = 13=${colors.brightMagenta}
|
|
411
|
+
palette = 14=${colors.brightCyan}
|
|
412
|
+
palette = 15=${colors.brightWhite}
|
|
413
|
+
|
|
414
|
+
# --- Window and Appearance ---
|
|
415
|
+
background-opacity = ${appearance.backgroundOpacity}
|
|
416
|
+
background-blur-radius = ${appearance.backgroundBlur}
|
|
417
|
+
macos-titlebar-style = transparent
|
|
418
|
+
window-padding-x = 10
|
|
419
|
+
window-padding-y = 8
|
|
420
|
+
window-save-state = always
|
|
421
|
+
window-theme = auto
|
|
422
|
+
|
|
423
|
+
# --- Cursor ---
|
|
424
|
+
cursor-style = ${appearance.cursorStyle}
|
|
425
|
+
cursor-style-blink = ${appearance.cursorBlink}
|
|
426
|
+
cursor-opacity = 0.8
|
|
427
|
+
|
|
428
|
+
# --- Mouse ---
|
|
429
|
+
mouse-hide-while-typing = true
|
|
430
|
+
copy-on-select = clipboard
|
|
431
|
+
|
|
432
|
+
# --- Quick Terminal ---
|
|
433
|
+
quick-terminal-position = top
|
|
434
|
+
quick-terminal-screen = mouse
|
|
435
|
+
quick-terminal-autohide = true
|
|
436
|
+
quick-terminal-animation-duration = 0.15
|
|
437
|
+
|
|
438
|
+
# --- Security ---
|
|
439
|
+
clipboard-paste-protection = true
|
|
440
|
+
clipboard-paste-bracketed-safe = true
|
|
441
|
+
|
|
442
|
+
# --- Shell Integration ---
|
|
443
|
+
shell-integration = zsh
|
|
444
|
+
|
|
445
|
+
# --- Claude Code Optimizations ---
|
|
446
|
+
initial-window = true
|
|
447
|
+
quit-after-last-window-closed = true
|
|
448
|
+
notify-on-command-finish = always
|
|
449
|
+
|
|
450
|
+
# --- Performance ---
|
|
451
|
+
scrollback-limit = 25000000
|
|
452
|
+
|
|
453
|
+
# --- Keybindings (Claude Code Workflow) ---
|
|
454
|
+
keybind = cmd+d=new_split:right
|
|
455
|
+
keybind = cmd+shift+enter=toggle_split_zoom
|
|
456
|
+
keybind = cmd+shift+f=toggle_split_zoom
|
|
457
|
+
`;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/configs/iterm2/profile.ts
|
|
461
|
+
function hexToRgbComponents(hex) {
|
|
462
|
+
const clean = hex.replace("#", "");
|
|
463
|
+
return {
|
|
464
|
+
r: parseInt(clean.substring(0, 2), 16) / 255,
|
|
465
|
+
g: parseInt(clean.substring(2, 4), 16) / 255,
|
|
466
|
+
b: parseInt(clean.substring(4, 6), 16) / 255
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function colorEntry(hex) {
|
|
470
|
+
const { r, g, b } = hexToRgbComponents(hex);
|
|
471
|
+
return {
|
|
472
|
+
"Red Component": r,
|
|
473
|
+
"Green Component": g,
|
|
474
|
+
"Blue Component": b,
|
|
475
|
+
"Alpha Component": 1,
|
|
476
|
+
"Color Space": "sRGB"
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function generateIterm2Profile(theme) {
|
|
480
|
+
const { colors, appearance } = theme;
|
|
481
|
+
const profile = {
|
|
482
|
+
"Name": `Den ${theme.displayName}`,
|
|
483
|
+
"Guid": `den-${theme.name}`,
|
|
484
|
+
"Normal Font": "MapleMono-NF-CN-Regular 14",
|
|
485
|
+
"Transparency": 1 - appearance.backgroundOpacity,
|
|
486
|
+
"Blur": appearance.backgroundBlur > 0,
|
|
487
|
+
"Blur Radius": appearance.backgroundBlur / 3,
|
|
488
|
+
"Background Color": colorEntry(colors.background),
|
|
489
|
+
"Foreground Color": colorEntry(colors.foreground),
|
|
490
|
+
"Cursor Color": colorEntry(colors.cursor),
|
|
491
|
+
"Selection Color": colorEntry(colors.selectionBackground),
|
|
492
|
+
"Selected Text Color": colorEntry(colors.selectionForeground),
|
|
493
|
+
"Ansi 0 Color": colorEntry(colors.black),
|
|
494
|
+
"Ansi 1 Color": colorEntry(colors.red),
|
|
495
|
+
"Ansi 2 Color": colorEntry(colors.green),
|
|
496
|
+
"Ansi 3 Color": colorEntry(colors.yellow),
|
|
497
|
+
"Ansi 4 Color": colorEntry(colors.blue),
|
|
498
|
+
"Ansi 5 Color": colorEntry(colors.magenta),
|
|
499
|
+
"Ansi 6 Color": colorEntry(colors.cyan),
|
|
500
|
+
"Ansi 7 Color": colorEntry(colors.white),
|
|
501
|
+
"Ansi 8 Color": colorEntry(colors.brightBlack),
|
|
502
|
+
"Ansi 9 Color": colorEntry(colors.brightRed),
|
|
503
|
+
"Ansi 10 Color": colorEntry(colors.brightGreen),
|
|
504
|
+
"Ansi 11 Color": colorEntry(colors.brightYellow),
|
|
505
|
+
"Ansi 12 Color": colorEntry(colors.brightBlue),
|
|
506
|
+
"Ansi 13 Color": colorEntry(colors.brightMagenta),
|
|
507
|
+
"Ansi 14 Color": colorEntry(colors.brightCyan),
|
|
508
|
+
"Ansi 15 Color": colorEntry(colors.brightWhite),
|
|
509
|
+
"Cursor Type": appearance.cursorStyle === "bar" ? 1 : appearance.cursorStyle === "underline" ? 2 : 0,
|
|
510
|
+
"Blinking Cursor": appearance.cursorBlink,
|
|
511
|
+
"Unlimited Scrollback": true,
|
|
512
|
+
"Mouse Reporting": true,
|
|
513
|
+
"Unicode Version": 9
|
|
514
|
+
};
|
|
515
|
+
return JSON.stringify(profile, null, 2);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// src/configs/kitty/config.ts
|
|
519
|
+
function generateKittyConfig(theme) {
|
|
520
|
+
const { colors, appearance } = theme;
|
|
521
|
+
const cursorShape = appearance.cursorStyle === "bar" ? "beam" : appearance.cursorStyle === "underline" ? "underline" : "block";
|
|
522
|
+
return `# ============================================
|
|
523
|
+
# Claude Den - Kitty Config
|
|
524
|
+
# Theme: ${theme.displayName}
|
|
525
|
+
# Generated by claude-den
|
|
526
|
+
# ============================================
|
|
527
|
+
|
|
528
|
+
# --- Typography ---
|
|
529
|
+
font_family Maple Mono NF CN
|
|
530
|
+
font_size 14.0
|
|
531
|
+
|
|
532
|
+
# --- Theme: ${theme.displayName} ---
|
|
533
|
+
background ${colors.background}
|
|
534
|
+
foreground ${colors.foreground}
|
|
535
|
+
cursor ${colors.cursor}
|
|
536
|
+
selection_background ${colors.selectionBackground}
|
|
537
|
+
selection_foreground ${colors.selectionForeground}
|
|
538
|
+
|
|
539
|
+
color0 ${colors.black}
|
|
540
|
+
color1 ${colors.red}
|
|
541
|
+
color2 ${colors.green}
|
|
542
|
+
color3 ${colors.yellow}
|
|
543
|
+
color4 ${colors.blue}
|
|
544
|
+
color5 ${colors.magenta}
|
|
545
|
+
color6 ${colors.cyan}
|
|
546
|
+
color7 ${colors.white}
|
|
547
|
+
color8 ${colors.brightBlack}
|
|
548
|
+
color9 ${colors.brightRed}
|
|
549
|
+
color10 ${colors.brightGreen}
|
|
550
|
+
color11 ${colors.brightYellow}
|
|
551
|
+
color12 ${colors.brightBlue}
|
|
552
|
+
color13 ${colors.brightMagenta}
|
|
553
|
+
color14 ${colors.brightCyan}
|
|
554
|
+
color15 ${colors.brightWhite}
|
|
555
|
+
|
|
556
|
+
# --- Appearance ---
|
|
557
|
+
background_opacity ${appearance.backgroundOpacity}
|
|
558
|
+
cursor_shape ${cursorShape}
|
|
559
|
+
cursor_blink_interval ${appearance.cursorBlink ? "0.5" : "0"}
|
|
560
|
+
hide_window_decorations titlebar-only
|
|
561
|
+
window_padding_width 8
|
|
562
|
+
|
|
563
|
+
# --- Performance ---
|
|
564
|
+
scrollback_lines 250000
|
|
565
|
+
|
|
566
|
+
# --- Keybindings (Claude Code Workflow) ---
|
|
567
|
+
map cmd+d launch --location=vsplit --cwd=current
|
|
568
|
+
map cmd+shift+enter toggle_layout stack
|
|
569
|
+
map cmd+shift+f toggle_layout stack
|
|
570
|
+
|
|
571
|
+
# --- Shell Integration ---
|
|
572
|
+
shell_integration enabled
|
|
573
|
+
`;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/configs/alacritty/config.ts
|
|
577
|
+
function generateAlacrittyConfig(theme) {
|
|
578
|
+
const { colors, appearance } = theme;
|
|
579
|
+
const cursorShape = appearance.cursorStyle === "bar" ? "Beam" : appearance.cursorStyle === "underline" ? "Underline" : "Block";
|
|
580
|
+
const config = {
|
|
581
|
+
font: {
|
|
582
|
+
normal: { family: "Maple Mono NF CN" },
|
|
583
|
+
size: 14
|
|
584
|
+
},
|
|
585
|
+
window: {
|
|
586
|
+
opacity: appearance.backgroundOpacity,
|
|
587
|
+
blur: appearance.backgroundBlur > 0,
|
|
588
|
+
padding: { x: 10, y: 8 },
|
|
589
|
+
decorations: "Transparent"
|
|
590
|
+
},
|
|
591
|
+
cursor: {
|
|
592
|
+
style: {
|
|
593
|
+
shape: cursorShape,
|
|
594
|
+
blinking: appearance.cursorBlink ? "On" : "Off"
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
colors: {
|
|
598
|
+
primary: {
|
|
599
|
+
background: colors.background,
|
|
600
|
+
foreground: colors.foreground
|
|
601
|
+
},
|
|
602
|
+
cursor: {
|
|
603
|
+
text: colors.background,
|
|
604
|
+
cursor: colors.cursor
|
|
605
|
+
},
|
|
606
|
+
selection: {
|
|
607
|
+
text: colors.selectionForeground,
|
|
608
|
+
background: colors.selectionBackground
|
|
609
|
+
},
|
|
610
|
+
normal: {
|
|
611
|
+
black: colors.black,
|
|
612
|
+
red: colors.red,
|
|
613
|
+
green: colors.green,
|
|
614
|
+
yellow: colors.yellow,
|
|
615
|
+
blue: colors.blue,
|
|
616
|
+
magenta: colors.magenta,
|
|
617
|
+
cyan: colors.cyan,
|
|
618
|
+
white: colors.white
|
|
619
|
+
},
|
|
620
|
+
bright: {
|
|
621
|
+
black: colors.brightBlack,
|
|
622
|
+
red: colors.brightRed,
|
|
623
|
+
green: colors.brightGreen,
|
|
624
|
+
yellow: colors.brightYellow,
|
|
625
|
+
blue: colors.brightBlue,
|
|
626
|
+
magenta: colors.brightMagenta,
|
|
627
|
+
cyan: colors.brightCyan,
|
|
628
|
+
white: colors.brightWhite
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
scrolling: {
|
|
632
|
+
history: 1e5
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
return `# ============================================
|
|
636
|
+
# Claude Den - Alacritty Config
|
|
637
|
+
# Theme: ${theme.displayName}
|
|
638
|
+
# Generated by claude-den
|
|
639
|
+
# ============================================
|
|
640
|
+
|
|
641
|
+
${toToml(config)}`;
|
|
642
|
+
}
|
|
643
|
+
function toToml(obj, prefix = "") {
|
|
644
|
+
const lines = [];
|
|
645
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
646
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
647
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
648
|
+
lines.push(`[${fullKey}]`);
|
|
649
|
+
for (const [subKey, subValue] of Object.entries(value)) {
|
|
650
|
+
if (typeof subValue === "object" && subValue !== null && !Array.isArray(subValue)) {
|
|
651
|
+
lines.push("");
|
|
652
|
+
lines.push(...toToml({ [subKey]: subValue }, fullKey).split("\n"));
|
|
653
|
+
} else {
|
|
654
|
+
lines.push(`${subKey} = ${formatTomlValue(subValue)}`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
lines.push("");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return lines.join("\n");
|
|
661
|
+
}
|
|
662
|
+
function formatTomlValue(value) {
|
|
663
|
+
if (typeof value === "string") return `"${value}"`;
|
|
664
|
+
if (typeof value === "number") return String(value);
|
|
665
|
+
if (typeof value === "boolean") return String(value);
|
|
666
|
+
return String(value);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/configs/starship/config.ts
|
|
670
|
+
function generateStarshipConfig(theme) {
|
|
671
|
+
const { colors } = theme;
|
|
672
|
+
return `# ============================================
|
|
673
|
+
# Claude Den - Starship Config
|
|
674
|
+
# Theme: ${theme.displayName}
|
|
675
|
+
# Generated by claude-den
|
|
676
|
+
# Place at: ~/.config/starship.toml
|
|
677
|
+
# ============================================
|
|
678
|
+
|
|
679
|
+
format = """
|
|
680
|
+
$directory$git_branch$git_status$nodejs$python$rust$golang$cmd_duration$line_break$character"""
|
|
681
|
+
|
|
682
|
+
[character]
|
|
683
|
+
success_symbol = "[\u276F](bold ${colors.green})"
|
|
684
|
+
error_symbol = "[\u276F](bold ${colors.red})"
|
|
685
|
+
vimcmd_symbol = "[\u276E](bold ${colors.blue})"
|
|
686
|
+
|
|
687
|
+
[directory]
|
|
688
|
+
style = "bold ${colors.blue}"
|
|
689
|
+
truncation_length = 3
|
|
690
|
+
truncate_to_repo = true
|
|
691
|
+
format = "[$path]($style)[$read_only]($read_only_style) "
|
|
692
|
+
|
|
693
|
+
[git_branch]
|
|
694
|
+
style = "bold ${colors.magenta}"
|
|
695
|
+
format = "[$symbol$branch(:$remote_branch)]($style) "
|
|
696
|
+
symbol = " "
|
|
697
|
+
|
|
698
|
+
[git_status]
|
|
699
|
+
style = "bold ${colors.red}"
|
|
700
|
+
format = '([$all_status$ahead_behind]($style) )'
|
|
701
|
+
conflicted = "="
|
|
702
|
+
ahead = "\u21E1\${count}"
|
|
703
|
+
behind = "\u21E3\${count}"
|
|
704
|
+
diverged = "\u21D5\u21E1\${ahead_count}\u21E3\${behind_count}"
|
|
705
|
+
untracked = "?\${count}"
|
|
706
|
+
stashed = "\\$\${count}"
|
|
707
|
+
modified = "!\${count}"
|
|
708
|
+
staged = "+\${count}"
|
|
709
|
+
deleted = "\u2718\${count}"
|
|
710
|
+
|
|
711
|
+
[nodejs]
|
|
712
|
+
style = "bold ${colors.green}"
|
|
713
|
+
format = "[$symbol($version)]($style) "
|
|
714
|
+
symbol = " "
|
|
715
|
+
|
|
716
|
+
[python]
|
|
717
|
+
style = "bold ${colors.yellow}"
|
|
718
|
+
format = '[$symbol\${pyenv_prefix}(\${version})]($style) '
|
|
719
|
+
symbol = " "
|
|
720
|
+
|
|
721
|
+
[rust]
|
|
722
|
+
style = "bold ${colors.red}"
|
|
723
|
+
format = "[$symbol($version)]($style) "
|
|
724
|
+
symbol = " "
|
|
725
|
+
|
|
726
|
+
[golang]
|
|
727
|
+
style = "bold ${colors.cyan}"
|
|
728
|
+
format = "[$symbol($version)]($style) "
|
|
729
|
+
symbol = " "
|
|
730
|
+
|
|
731
|
+
[cmd_duration]
|
|
732
|
+
style = "bold ${colors.yellow}"
|
|
733
|
+
min_time = 2_000
|
|
734
|
+
format = "[$duration]($style) "
|
|
735
|
+
show_milliseconds = false
|
|
736
|
+
|
|
737
|
+
[line_break]
|
|
738
|
+
disabled = false
|
|
739
|
+
`;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// src/configs/tmux/config.ts
|
|
743
|
+
var LAYOUTS = /* @__PURE__ */ new Map([
|
|
744
|
+
[
|
|
745
|
+
"coding",
|
|
746
|
+
{
|
|
747
|
+
name: "coding",
|
|
748
|
+
description: "Claude Code left, editor/shell right",
|
|
749
|
+
panes: [
|
|
750
|
+
{ command: "claude", split: "horizontal", size: "60%" },
|
|
751
|
+
{ command: "$SHELL", split: "vertical" }
|
|
752
|
+
]
|
|
753
|
+
}
|
|
754
|
+
],
|
|
755
|
+
[
|
|
756
|
+
"parallel",
|
|
757
|
+
{
|
|
758
|
+
name: "parallel",
|
|
759
|
+
description: "2x2 grid for parallel Claude agents",
|
|
760
|
+
panes: [
|
|
761
|
+
{ command: "claude", split: "horizontal", size: "50%" },
|
|
762
|
+
{ command: "claude", split: "vertical", size: "50%" },
|
|
763
|
+
{ command: "claude", split: "horizontal", size: "50%" },
|
|
764
|
+
{ command: "claude", split: "vertical" }
|
|
765
|
+
]
|
|
766
|
+
}
|
|
767
|
+
],
|
|
768
|
+
[
|
|
769
|
+
"monitor",
|
|
770
|
+
{
|
|
771
|
+
name: "monitor",
|
|
772
|
+
description: "Claude + logs + git + system monitor",
|
|
773
|
+
panes: [
|
|
774
|
+
{ command: "claude", split: "horizontal", size: "60%" },
|
|
775
|
+
{ command: "lazygit 2>/dev/null || git log --oneline -20", split: "vertical", size: "50%" },
|
|
776
|
+
{ command: "btop 2>/dev/null || top", split: "vertical" }
|
|
777
|
+
]
|
|
778
|
+
}
|
|
779
|
+
]
|
|
780
|
+
]);
|
|
781
|
+
function generateTmuxConfig(theme) {
|
|
782
|
+
const { colors } = theme;
|
|
783
|
+
return `# ============================================
|
|
784
|
+
# Claude Den - tmux Config
|
|
785
|
+
# Theme: ${theme.displayName}
|
|
786
|
+
# Generated by claude-den
|
|
787
|
+
# Place at: ~/.tmux.conf
|
|
788
|
+
# ============================================
|
|
789
|
+
|
|
790
|
+
# --- General ---
|
|
791
|
+
set -g default-terminal "tmux-256color"
|
|
792
|
+
set -ag terminal-overrides ",xterm-256color:RGB"
|
|
793
|
+
set -g mouse on
|
|
794
|
+
set -g history-limit 250000
|
|
795
|
+
set -g base-index 1
|
|
796
|
+
setw -g pane-base-index 1
|
|
797
|
+
set -g renumber-windows on
|
|
798
|
+
set -s escape-time 0
|
|
799
|
+
set -g focus-events on
|
|
800
|
+
|
|
801
|
+
# --- Theme: ${theme.displayName} ---
|
|
802
|
+
set -g status-style "bg=${colors.background},fg=${colors.foreground}"
|
|
803
|
+
set -g status-left "#[bg=${colors.blue},fg=${colors.background},bold] #S #[default] "
|
|
804
|
+
set -g status-right "#[fg=${colors.brightBlack}]%H:%M #[fg=${colors.magenta}]#h "
|
|
805
|
+
set -g status-left-length 30
|
|
806
|
+
set -g status-right-length 50
|
|
807
|
+
|
|
808
|
+
setw -g window-status-format "#[fg=${colors.brightBlack}] #I:#W "
|
|
809
|
+
setw -g window-status-current-format "#[bg=${colors.blue},fg=${colors.background},bold] #I:#W "
|
|
810
|
+
|
|
811
|
+
set -g pane-border-style "fg=${colors.brightBlack}"
|
|
812
|
+
set -g pane-active-border-style "fg=${colors.blue}"
|
|
813
|
+
set -g message-style "bg=${colors.yellow},fg=${colors.background}"
|
|
814
|
+
|
|
815
|
+
# --- Keybindings ---
|
|
816
|
+
set -g prefix C-a
|
|
817
|
+
unbind C-b
|
|
818
|
+
bind C-a send-prefix
|
|
819
|
+
|
|
820
|
+
# Split panes (Claude Code workflow)
|
|
821
|
+
bind | split-window -h -c "#{pane_current_path}"
|
|
822
|
+
bind - split-window -v -c "#{pane_current_path}"
|
|
823
|
+
unbind '"'
|
|
824
|
+
unbind %
|
|
825
|
+
|
|
826
|
+
# Navigate panes with Alt+arrow
|
|
827
|
+
bind -n M-Left select-pane -L
|
|
828
|
+
bind -n M-Right select-pane -R
|
|
829
|
+
bind -n M-Up select-pane -U
|
|
830
|
+
bind -n M-Down select-pane -D
|
|
831
|
+
|
|
832
|
+
# Resize panes
|
|
833
|
+
bind -r H resize-pane -L 5
|
|
834
|
+
bind -r J resize-pane -D 5
|
|
835
|
+
bind -r K resize-pane -U 5
|
|
836
|
+
bind -r L resize-pane -R 5
|
|
837
|
+
|
|
838
|
+
# Reload config
|
|
839
|
+
bind r source-file ~/.tmux.conf \\; display "Config reloaded!"
|
|
840
|
+
|
|
841
|
+
# New window in current path
|
|
842
|
+
bind c new-window -c "#{pane_current_path}"
|
|
843
|
+
`;
|
|
844
|
+
}
|
|
845
|
+
function generateTmuxLayoutScript(layout) {
|
|
846
|
+
const sessionName = `den-${layout.name}`;
|
|
847
|
+
const lines = [
|
|
848
|
+
"#!/usr/bin/env bash",
|
|
849
|
+
`# Claude Den Layout: ${layout.name} - ${layout.description}`,
|
|
850
|
+
"",
|
|
851
|
+
`SESSION="${sessionName}"`,
|
|
852
|
+
"",
|
|
853
|
+
"# Kill existing session if any",
|
|
854
|
+
`tmux has-session -t $SESSION 2>/dev/null && tmux kill-session -t $SESSION`,
|
|
855
|
+
"",
|
|
856
|
+
"# Create new session",
|
|
857
|
+
`tmux new-session -d -s $SESSION -x $(tput cols) -y $(tput lines)`,
|
|
858
|
+
""
|
|
859
|
+
];
|
|
860
|
+
for (let i = 0; i < layout.panes.length; i++) {
|
|
861
|
+
const pane = layout.panes[i];
|
|
862
|
+
if (i > 0) {
|
|
863
|
+
const splitFlag = pane.split === "horizontal" ? "-h" : "-v";
|
|
864
|
+
const sizeFlag = pane.size ? `-p ${parseInt(pane.size)}` : "";
|
|
865
|
+
lines.push(`tmux split-window ${splitFlag} ${sizeFlag} -t $SESSION`);
|
|
866
|
+
}
|
|
867
|
+
lines.push(`tmux send-keys -t $SESSION '${pane.command}' Enter`);
|
|
868
|
+
lines.push("");
|
|
869
|
+
}
|
|
870
|
+
lines.push("# Select first pane");
|
|
871
|
+
lines.push(`tmux select-pane -t $SESSION:0.0`);
|
|
872
|
+
lines.push("");
|
|
873
|
+
lines.push("# Attach to session");
|
|
874
|
+
lines.push(`tmux attach-session -t $SESSION`);
|
|
875
|
+
return lines.join("\n");
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// src/cli/backup.ts
|
|
879
|
+
import {
|
|
880
|
+
copyFileSync,
|
|
881
|
+
existsSync as existsSync3,
|
|
882
|
+
mkdirSync,
|
|
883
|
+
readFileSync,
|
|
884
|
+
writeFileSync
|
|
885
|
+
} from "fs";
|
|
886
|
+
import { join, basename, dirname } from "path";
|
|
887
|
+
import { homedir as homedir2 } from "os";
|
|
888
|
+
import chalk from "chalk";
|
|
889
|
+
var BACKUP_DIR = join(homedir2(), ".config", "claude-den", "backups");
|
|
890
|
+
function ensureBackupDir() {
|
|
891
|
+
if (!existsSync3(BACKUP_DIR)) {
|
|
892
|
+
mkdirSync(BACKUP_DIR, { recursive: true });
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
function getManifestPath() {
|
|
896
|
+
return join(BACKUP_DIR, "manifest.json");
|
|
897
|
+
}
|
|
898
|
+
function readManifest() {
|
|
899
|
+
const manifestPath = getManifestPath();
|
|
900
|
+
if (!existsSync3(manifestPath)) return null;
|
|
901
|
+
const raw = readFileSync(manifestPath, "utf-8");
|
|
902
|
+
return JSON.parse(raw);
|
|
903
|
+
}
|
|
904
|
+
function writeManifest(manifest) {
|
|
905
|
+
ensureBackupDir();
|
|
906
|
+
writeFileSync(getManifestPath(), JSON.stringify(manifest, null, 2), "utf-8");
|
|
907
|
+
}
|
|
908
|
+
function backupFile(sourcePath) {
|
|
909
|
+
ensureBackupDir();
|
|
910
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
911
|
+
const backupName = `${basename(sourcePath)}.${timestamp}.bak`;
|
|
912
|
+
const backupPath = join(BACKUP_DIR, backupName);
|
|
913
|
+
copyFileSync(sourcePath, backupPath);
|
|
914
|
+
const manifest = readManifest() ?? {
|
|
915
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
916
|
+
files: []
|
|
917
|
+
};
|
|
918
|
+
const updatedManifest = {
|
|
919
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
920
|
+
files: [
|
|
921
|
+
...manifest.files,
|
|
922
|
+
{ source: sourcePath, backup: backupPath }
|
|
923
|
+
]
|
|
924
|
+
};
|
|
925
|
+
writeManifest(updatedManifest);
|
|
926
|
+
return backupPath;
|
|
927
|
+
}
|
|
928
|
+
function registerBackupCommand(program2) {
|
|
929
|
+
program2.command("backup").description("Backup current terminal configurations").action(() => {
|
|
930
|
+
console.log(chalk.bold.blue("\n Claude Den Backup\n"));
|
|
931
|
+
const home = homedir2();
|
|
932
|
+
const configFiles = [
|
|
933
|
+
join(home, ".config", "ghostty", "config"),
|
|
934
|
+
join(home, ".config", "kitty", "kitty.conf"),
|
|
935
|
+
join(home, ".config", "alacritty", "alacritty.toml"),
|
|
936
|
+
join(home, ".config", "starship.toml"),
|
|
937
|
+
join(home, ".tmux.conf")
|
|
938
|
+
];
|
|
939
|
+
let backupCount = 0;
|
|
940
|
+
for (const file of configFiles) {
|
|
941
|
+
if (existsSync3(file)) {
|
|
942
|
+
const backupPath = backupFile(file);
|
|
943
|
+
console.log(chalk.green(` \u2713 ${file}`));
|
|
944
|
+
console.log(chalk.dim(` \u2192 ${backupPath}`));
|
|
945
|
+
backupCount++;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (backupCount === 0) {
|
|
949
|
+
console.log(chalk.dim(" No config files found to backup.\n"));
|
|
950
|
+
} else {
|
|
951
|
+
console.log(
|
|
952
|
+
chalk.green(`
|
|
953
|
+
Backed up ${backupCount} file(s) to ${BACKUP_DIR}
|
|
954
|
+
`)
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
function registerRestoreCommand(program2) {
|
|
960
|
+
program2.command("restore").description("Restore terminal configurations from backup").action(() => {
|
|
961
|
+
console.log(chalk.bold.blue("\n Claude Den Restore\n"));
|
|
962
|
+
const manifest = readManifest();
|
|
963
|
+
if (!manifest || manifest.files.length === 0) {
|
|
964
|
+
console.log(chalk.dim(" No backups found.\n"));
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
console.log(
|
|
968
|
+
chalk.dim(` Backup from: ${manifest.timestamp}
|
|
969
|
+
`)
|
|
970
|
+
);
|
|
971
|
+
let restoreCount = 0;
|
|
972
|
+
for (const entry of manifest.files) {
|
|
973
|
+
if (existsSync3(entry.backup)) {
|
|
974
|
+
const dir = dirname(entry.source);
|
|
975
|
+
if (!existsSync3(dir)) {
|
|
976
|
+
mkdirSync(dir, { recursive: true });
|
|
977
|
+
}
|
|
978
|
+
copyFileSync(entry.backup, entry.source);
|
|
979
|
+
console.log(chalk.green(` \u2713 Restored ${entry.source}`));
|
|
980
|
+
restoreCount++;
|
|
981
|
+
} else {
|
|
982
|
+
console.log(
|
|
983
|
+
chalk.yellow(` ! Backup missing: ${entry.backup}`)
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
console.log(
|
|
988
|
+
chalk.green(`
|
|
989
|
+
Restored ${restoreCount} file(s).
|
|
990
|
+
`)
|
|
991
|
+
);
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/cli/init.ts
|
|
996
|
+
var CONFIG_DESTINATIONS = {
|
|
997
|
+
ghostty: (home) => join2(home, ".config", "ghostty", "config"),
|
|
998
|
+
kitty: (home) => join2(home, ".config", "kitty", "kitty.conf"),
|
|
999
|
+
alacritty: (home) => join2(home, ".config", "alacritty", "alacritty.toml")
|
|
1000
|
+
};
|
|
1001
|
+
var CONFIG_GENERATORS = {
|
|
1002
|
+
ghostty: generateGhosttyConfig,
|
|
1003
|
+
iterm2: generateIterm2Profile,
|
|
1004
|
+
kitty: generateKittyConfig,
|
|
1005
|
+
alacritty: generateAlacrittyConfig
|
|
1006
|
+
};
|
|
1007
|
+
function writeConfig(path, content) {
|
|
1008
|
+
const dir = dirname2(path);
|
|
1009
|
+
if (!existsSync4(dir)) {
|
|
1010
|
+
mkdirSync2(dir, { recursive: true });
|
|
1011
|
+
}
|
|
1012
|
+
writeFileSync2(path, content, "utf-8");
|
|
1013
|
+
}
|
|
1014
|
+
function registerInitCommand(program2) {
|
|
1015
|
+
program2.command("init").description("Interactive setup wizard for Claude Code terminal experience").option("-t, --theme <name>", "Theme name (skip theme selection)").option("--no-starship", "Skip Starship configuration").option("--no-tmux", "Skip tmux configuration").action(async (options) => {
|
|
1016
|
+
console.log(
|
|
1017
|
+
chalk2.bold.blue("\n Claude Den\n")
|
|
1018
|
+
);
|
|
1019
|
+
console.log(
|
|
1020
|
+
chalk2.dim(" Your cozy terminal den for Claude Code\n")
|
|
1021
|
+
);
|
|
1022
|
+
const terminal = detectTerminal();
|
|
1023
|
+
const capabilities = detectCapabilities();
|
|
1024
|
+
const os = detectOS();
|
|
1025
|
+
console.log(chalk2.green(" Detected environment:"));
|
|
1026
|
+
console.log(` Terminal: ${chalk2.bold(terminal.name)}`);
|
|
1027
|
+
console.log(` OS: ${chalk2.bold(os.type)} (${os.arch})`);
|
|
1028
|
+
console.log(` Shell: ${chalk2.bold(os.shell)}`);
|
|
1029
|
+
console.log(
|
|
1030
|
+
` TrueColor: ${capabilities.trueColor ? chalk2.green("Yes") : chalk2.yellow("No")}`
|
|
1031
|
+
);
|
|
1032
|
+
console.log("");
|
|
1033
|
+
const installed = listInstalledTerminals();
|
|
1034
|
+
const terminalChoices = [
|
|
1035
|
+
...terminal.type !== "unknown" ? [
|
|
1036
|
+
{
|
|
1037
|
+
name: `${terminal.name} (current)`,
|
|
1038
|
+
value: terminal.type
|
|
1039
|
+
}
|
|
1040
|
+
] : [],
|
|
1041
|
+
...installed.filter((t) => t.type !== terminal.type).map((t) => ({ name: t.name, value: t.type }))
|
|
1042
|
+
];
|
|
1043
|
+
if (terminalChoices.length === 0) {
|
|
1044
|
+
terminalChoices.push({
|
|
1045
|
+
name: "Ghostty (recommended for Claude Code)",
|
|
1046
|
+
value: "ghostty"
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
const { selectedTerminal } = await inquirer.prompt([
|
|
1050
|
+
{
|
|
1051
|
+
type: "list",
|
|
1052
|
+
name: "selectedTerminal",
|
|
1053
|
+
message: "Which terminal do you want to configure?",
|
|
1054
|
+
choices: terminalChoices
|
|
1055
|
+
}
|
|
1056
|
+
]);
|
|
1057
|
+
const themes = listThemes();
|
|
1058
|
+
let selectedTheme;
|
|
1059
|
+
if (options.theme) {
|
|
1060
|
+
selectedTheme = getThemeOrDefault(options.theme);
|
|
1061
|
+
console.log(` Theme: ${chalk2.bold(selectedTheme.displayName)}
|
|
1062
|
+
`);
|
|
1063
|
+
} else {
|
|
1064
|
+
const { themeName } = await inquirer.prompt([
|
|
1065
|
+
{
|
|
1066
|
+
type: "list",
|
|
1067
|
+
name: "themeName",
|
|
1068
|
+
message: "Choose a color theme:",
|
|
1069
|
+
choices: themes.map((t) => ({
|
|
1070
|
+
name: `${t.displayName} - ${chalk2.dim(t.description)}`,
|
|
1071
|
+
value: t.name
|
|
1072
|
+
}))
|
|
1073
|
+
}
|
|
1074
|
+
]);
|
|
1075
|
+
selectedTheme = getThemeOrDefault(themeName);
|
|
1076
|
+
}
|
|
1077
|
+
const { setupStarship, setupTmux } = await inquirer.prompt([
|
|
1078
|
+
{
|
|
1079
|
+
type: "confirm",
|
|
1080
|
+
name: "setupStarship",
|
|
1081
|
+
message: "Setup Starship prompt with Claude Code theme?",
|
|
1082
|
+
default: options.starship !== false,
|
|
1083
|
+
when: () => options.starship !== false
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
type: "confirm",
|
|
1087
|
+
name: "setupTmux",
|
|
1088
|
+
message: "Setup tmux with Claude Code workflow keybindings?",
|
|
1089
|
+
default: options.tmux !== false,
|
|
1090
|
+
when: () => options.tmux !== false
|
|
1091
|
+
}
|
|
1092
|
+
]);
|
|
1093
|
+
console.log(chalk2.blue("\n Installing configurations...\n"));
|
|
1094
|
+
const home = homedir3();
|
|
1095
|
+
const generator = CONFIG_GENERATORS[selectedTerminal];
|
|
1096
|
+
if (generator) {
|
|
1097
|
+
const configContent = generator(selectedTheme);
|
|
1098
|
+
const getDestination = CONFIG_DESTINATIONS[selectedTerminal];
|
|
1099
|
+
if (getDestination) {
|
|
1100
|
+
const destPath = getDestination(home);
|
|
1101
|
+
if (existsSync4(destPath)) {
|
|
1102
|
+
backupFile(destPath);
|
|
1103
|
+
console.log(chalk2.dim(` Backed up existing config`));
|
|
1104
|
+
}
|
|
1105
|
+
writeConfig(destPath, configContent);
|
|
1106
|
+
console.log(
|
|
1107
|
+
chalk2.green(` \u2713 ${selectedTerminal} config \u2192 ${destPath}`)
|
|
1108
|
+
);
|
|
1109
|
+
} else if (selectedTerminal === "iterm2") {
|
|
1110
|
+
const profilePath = join2(
|
|
1111
|
+
home,
|
|
1112
|
+
".config",
|
|
1113
|
+
"claude-den",
|
|
1114
|
+
`iterm2-${selectedTheme.name}.json`
|
|
1115
|
+
);
|
|
1116
|
+
writeConfig(profilePath, configContent);
|
|
1117
|
+
console.log(
|
|
1118
|
+
chalk2.green(
|
|
1119
|
+
` \u2713 iTerm2 profile \u2192 ${profilePath}`
|
|
1120
|
+
)
|
|
1121
|
+
);
|
|
1122
|
+
console.log(
|
|
1123
|
+
chalk2.dim(
|
|
1124
|
+
" Import via iTerm2 \u2192 Settings \u2192 Profiles \u2192 Other Actions \u2192 Import JSON Profiles"
|
|
1125
|
+
)
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (setupStarship !== false) {
|
|
1130
|
+
const starshipPath = join2(home, ".config", "starship.toml");
|
|
1131
|
+
if (existsSync4(starshipPath)) {
|
|
1132
|
+
backupFile(starshipPath);
|
|
1133
|
+
}
|
|
1134
|
+
writeConfig(starshipPath, generateStarshipConfig(selectedTheme));
|
|
1135
|
+
console.log(chalk2.green(` \u2713 Starship config \u2192 ${starshipPath}`));
|
|
1136
|
+
const shellRcPath = os.shell === "zsh" ? join2(home, ".zshrc") : os.shell === "bash" ? join2(home, ".bashrc") : null;
|
|
1137
|
+
if (shellRcPath && existsSync4(shellRcPath)) {
|
|
1138
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
1139
|
+
const rcContent = readFileSync2(shellRcPath, "utf-8");
|
|
1140
|
+
if (!rcContent.includes("starship init")) {
|
|
1141
|
+
console.log(
|
|
1142
|
+
chalk2.yellow(
|
|
1143
|
+
`
|
|
1144
|
+
Note: Add this to your ${shellRcPath}:`
|
|
1145
|
+
)
|
|
1146
|
+
);
|
|
1147
|
+
console.log(
|
|
1148
|
+
chalk2.dim(` eval "$(starship init ${os.shell})"
|
|
1149
|
+
`)
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
if (setupTmux !== false) {
|
|
1155
|
+
const tmuxPath = join2(home, ".tmux.conf");
|
|
1156
|
+
if (existsSync4(tmuxPath)) {
|
|
1157
|
+
backupFile(tmuxPath);
|
|
1158
|
+
}
|
|
1159
|
+
writeConfig(tmuxPath, generateTmuxConfig(selectedTheme));
|
|
1160
|
+
console.log(chalk2.green(` \u2713 tmux config \u2192 ${tmuxPath}`));
|
|
1161
|
+
}
|
|
1162
|
+
console.log(chalk2.bold.green("\n Setup complete!\n"));
|
|
1163
|
+
console.log(chalk2.dim(" Quick tips:"));
|
|
1164
|
+
console.log(chalk2.dim(" \u2022 den theme <name> Switch theme"));
|
|
1165
|
+
console.log(chalk2.dim(" \u2022 den layout coding Launch coding layout"));
|
|
1166
|
+
console.log(chalk2.dim(" \u2022 den doctor Check terminal setup"));
|
|
1167
|
+
console.log("");
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// src/cli/theme.ts
|
|
1172
|
+
import chalk3 from "chalk";
|
|
1173
|
+
function renderThemePreview(theme) {
|
|
1174
|
+
const { colors } = theme;
|
|
1175
|
+
console.log(chalk3.bold(`
|
|
1176
|
+
${theme.displayName}`));
|
|
1177
|
+
console.log(chalk3.dim(` ${theme.description} (by ${theme.author})
|
|
1178
|
+
`));
|
|
1179
|
+
const normalColors = [
|
|
1180
|
+
["black", colors.black],
|
|
1181
|
+
["red", colors.red],
|
|
1182
|
+
["green", colors.green],
|
|
1183
|
+
["yellow", colors.yellow],
|
|
1184
|
+
["blue", colors.blue],
|
|
1185
|
+
["magenta", colors.magenta],
|
|
1186
|
+
["cyan", colors.cyan],
|
|
1187
|
+
["white", colors.white]
|
|
1188
|
+
];
|
|
1189
|
+
const brightColors = [
|
|
1190
|
+
["brBlack", colors.brightBlack],
|
|
1191
|
+
["brRed", colors.brightRed],
|
|
1192
|
+
["brGreen", colors.brightGreen],
|
|
1193
|
+
["brYellow", colors.brightYellow],
|
|
1194
|
+
["brBlue", colors.brightBlue],
|
|
1195
|
+
["brMagenta", colors.brightMagenta],
|
|
1196
|
+
["brCyan", colors.brightCyan],
|
|
1197
|
+
["brWhite", colors.brightWhite]
|
|
1198
|
+
];
|
|
1199
|
+
const swatch = (hex) => chalk3.bgHex(hex)(" ");
|
|
1200
|
+
console.log(
|
|
1201
|
+
" Normal: " + normalColors.map(([, c]) => swatch(c)).join(" ")
|
|
1202
|
+
);
|
|
1203
|
+
console.log(
|
|
1204
|
+
" Bright: " + brightColors.map(([, c]) => swatch(c)).join(" ")
|
|
1205
|
+
);
|
|
1206
|
+
console.log(
|
|
1207
|
+
`
|
|
1208
|
+
BG: ${chalk3.bgHex(colors.background)(" ")} FG: ${chalk3.hex(colors.foreground)("Hello Claude")}`
|
|
1209
|
+
);
|
|
1210
|
+
console.log("");
|
|
1211
|
+
}
|
|
1212
|
+
function registerThemeCommand(program2) {
|
|
1213
|
+
const themeCmd = program2.command("theme").description("Manage terminal themes");
|
|
1214
|
+
themeCmd.command("list").description("List all available themes").action(() => {
|
|
1215
|
+
const themes = listThemes();
|
|
1216
|
+
console.log(chalk3.bold.blue("\n Available Themes\n"));
|
|
1217
|
+
for (const theme of themes) {
|
|
1218
|
+
const colorDots = [
|
|
1219
|
+
theme.colors.red,
|
|
1220
|
+
theme.colors.green,
|
|
1221
|
+
theme.colors.blue,
|
|
1222
|
+
theme.colors.magenta,
|
|
1223
|
+
theme.colors.cyan
|
|
1224
|
+
].map((c) => chalk3.hex(c)("\u25CF")).join(" ");
|
|
1225
|
+
console.log(
|
|
1226
|
+
` ${chalk3.bold(theme.name.padEnd(20))} ${colorDots} ${chalk3.dim(theme.description)}`
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
console.log("");
|
|
1230
|
+
});
|
|
1231
|
+
themeCmd.command("preview <name>").description("Preview a theme's colors").action((name) => {
|
|
1232
|
+
const theme = getTheme(name);
|
|
1233
|
+
if (!theme) {
|
|
1234
|
+
console.log(chalk3.red(` Theme "${name}" not found.`));
|
|
1235
|
+
console.log(
|
|
1236
|
+
chalk3.dim(" Run `den theme list` to see available themes.\n")
|
|
1237
|
+
);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
renderThemePreview(theme);
|
|
1241
|
+
});
|
|
1242
|
+
themeCmd.command("apply <name>").description("Apply theme to all configured terminals").action(async (name) => {
|
|
1243
|
+
const theme = getTheme(name);
|
|
1244
|
+
if (!theme) {
|
|
1245
|
+
console.log(chalk3.red(` Theme "${name}" not found.
|
|
1246
|
+
`));
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
console.log(
|
|
1250
|
+
chalk3.blue(`
|
|
1251
|
+
Applying theme: ${chalk3.bold(theme.displayName)}
|
|
1252
|
+
`)
|
|
1253
|
+
);
|
|
1254
|
+
console.log(
|
|
1255
|
+
chalk3.dim(` Run \`den init --theme ${name}\` to apply to specific terminals.
|
|
1256
|
+
`)
|
|
1257
|
+
);
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// src/cli/layout.ts
|
|
1262
|
+
import { execSync, execFileSync } from "child_process";
|
|
1263
|
+
import { writeFileSync as writeFileSync3, unlinkSync } from "fs";
|
|
1264
|
+
import { join as join3 } from "path";
|
|
1265
|
+
import { tmpdir } from "os";
|
|
1266
|
+
import chalk4 from "chalk";
|
|
1267
|
+
function registerLayoutCommand(program2) {
|
|
1268
|
+
const layoutCmd = program2.command("layout").description("Launch tmux layouts for Claude Code workflows");
|
|
1269
|
+
layoutCmd.command("list").description("List available layouts").action(() => {
|
|
1270
|
+
console.log(chalk4.bold.blue("\n Available Layouts\n"));
|
|
1271
|
+
for (const [name, layout] of LAYOUTS) {
|
|
1272
|
+
console.log(
|
|
1273
|
+
` ${chalk4.bold(name.padEnd(15))} ${chalk4.dim(layout.description)}`
|
|
1274
|
+
);
|
|
1275
|
+
console.log(
|
|
1276
|
+
chalk4.dim(
|
|
1277
|
+
` ${"".padEnd(15)} ${layout.panes.length} panes`
|
|
1278
|
+
)
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
console.log("");
|
|
1282
|
+
});
|
|
1283
|
+
layoutCmd.command("start <name>").description("Start a tmux layout").action((name) => {
|
|
1284
|
+
const layout = LAYOUTS.get(name);
|
|
1285
|
+
if (!layout) {
|
|
1286
|
+
console.log(chalk4.red(` Layout "${name}" not found.`));
|
|
1287
|
+
console.log(
|
|
1288
|
+
chalk4.dim(" Run `den layout list` to see available layouts.\n")
|
|
1289
|
+
);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
execFileSync("which", ["tmux"], { stdio: "pipe" });
|
|
1294
|
+
} catch {
|
|
1295
|
+
console.log(
|
|
1296
|
+
chalk4.red(" tmux is not installed. Install with: brew install tmux\n")
|
|
1297
|
+
);
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
const script = generateTmuxLayoutScript(layout);
|
|
1301
|
+
console.log(
|
|
1302
|
+
chalk4.blue(
|
|
1303
|
+
`
|
|
1304
|
+
Launching layout: ${chalk4.bold(layout.name)}
|
|
1305
|
+
`
|
|
1306
|
+
)
|
|
1307
|
+
);
|
|
1308
|
+
const scriptPath = join3(tmpdir(), `claude-den-layout-${Date.now()}.sh`);
|
|
1309
|
+
try {
|
|
1310
|
+
writeFileSync3(scriptPath, script, { mode: 493 });
|
|
1311
|
+
execSync(`bash "${scriptPath}"`, { stdio: "inherit" });
|
|
1312
|
+
} catch {
|
|
1313
|
+
} finally {
|
|
1314
|
+
try {
|
|
1315
|
+
unlinkSync(scriptPath);
|
|
1316
|
+
} catch {
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// src/cli/doctor.ts
|
|
1323
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1324
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
1325
|
+
import { join as join4 } from "path";
|
|
1326
|
+
import { homedir as homedir4 } from "os";
|
|
1327
|
+
import chalk5 from "chalk";
|
|
1328
|
+
function check(name, test) {
|
|
1329
|
+
try {
|
|
1330
|
+
const result = test();
|
|
1331
|
+
return { name, ...result };
|
|
1332
|
+
} catch {
|
|
1333
|
+
return { name, status: "fail", message: "Check failed" };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
var ALLOWED_COMMANDS = /* @__PURE__ */ new Set(["claude", "starship", "tmux", "git"]);
|
|
1337
|
+
function commandExists(cmd) {
|
|
1338
|
+
if (!ALLOWED_COMMANDS.has(cmd)) return false;
|
|
1339
|
+
try {
|
|
1340
|
+
execFileSync2("which", [cmd], { stdio: "pipe" });
|
|
1341
|
+
return true;
|
|
1342
|
+
} catch {
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function formatCheck(result) {
|
|
1347
|
+
const icon = result.status === "ok" ? chalk5.green("\u2713") : result.status === "warn" ? chalk5.yellow("!") : chalk5.red("\u2717");
|
|
1348
|
+
return ` ${icon} ${result.name.padEnd(25)} ${chalk5.dim(result.message)}`;
|
|
1349
|
+
}
|
|
1350
|
+
function registerDoctorCommand(program2) {
|
|
1351
|
+
program2.command("doctor").description("Diagnose your terminal setup for Claude Code").action(() => {
|
|
1352
|
+
console.log(chalk5.bold.blue("\n Claude Den Doctor\n"));
|
|
1353
|
+
const home = homedir4();
|
|
1354
|
+
const terminal = detectTerminal();
|
|
1355
|
+
const capabilities = detectCapabilities();
|
|
1356
|
+
const os = detectOS();
|
|
1357
|
+
const checks = [
|
|
1358
|
+
// Terminal
|
|
1359
|
+
check(
|
|
1360
|
+
"Terminal",
|
|
1361
|
+
() => terminal.type !== "unknown" ? { status: "ok", message: `${terminal.name} detected` } : { status: "warn", message: "Unknown terminal" }
|
|
1362
|
+
),
|
|
1363
|
+
// TrueColor
|
|
1364
|
+
check(
|
|
1365
|
+
"TrueColor support",
|
|
1366
|
+
() => capabilities.trueColor ? { status: "ok", message: "24-bit color supported" } : { status: "warn", message: "Limited to 256 colors" }
|
|
1367
|
+
),
|
|
1368
|
+
// Unicode
|
|
1369
|
+
check(
|
|
1370
|
+
"Unicode support",
|
|
1371
|
+
() => capabilities.unicode ? { status: "ok", message: "UTF-8 enabled" } : { status: "warn", message: "UTF-8 not detected in locale" }
|
|
1372
|
+
),
|
|
1373
|
+
// Claude Code
|
|
1374
|
+
check(
|
|
1375
|
+
"Claude Code",
|
|
1376
|
+
() => commandExists("claude") ? { status: "ok", message: "Installed" } : { status: "fail", message: "Not found. Install: npm i -g @anthropic-ai/claude-code" }
|
|
1377
|
+
),
|
|
1378
|
+
// Starship
|
|
1379
|
+
check("Starship", () => {
|
|
1380
|
+
if (!commandExists("starship")) {
|
|
1381
|
+
return { status: "warn", message: "Not installed. Install: brew install starship" };
|
|
1382
|
+
}
|
|
1383
|
+
const configPath = join4(home, ".config", "starship.toml");
|
|
1384
|
+
return existsSync5(configPath) ? { status: "ok", message: "Installed and configured" } : { status: "warn", message: "Installed but no config found" };
|
|
1385
|
+
}),
|
|
1386
|
+
// tmux
|
|
1387
|
+
check(
|
|
1388
|
+
"tmux",
|
|
1389
|
+
() => commandExists("tmux") ? { status: "ok", message: "Installed" } : { status: "warn", message: "Not installed. Install: brew install tmux" }
|
|
1390
|
+
),
|
|
1391
|
+
// Font
|
|
1392
|
+
check("Maple Mono NF CN", () => {
|
|
1393
|
+
const fontPaths = [
|
|
1394
|
+
join4(home, "Library", "Fonts"),
|
|
1395
|
+
"/Library/Fonts",
|
|
1396
|
+
"/System/Library/Fonts"
|
|
1397
|
+
];
|
|
1398
|
+
for (const fontPath of fontPaths) {
|
|
1399
|
+
try {
|
|
1400
|
+
const files = readdirSync(fontPath);
|
|
1401
|
+
if (files.some((f) => f.includes("MapleMono"))) {
|
|
1402
|
+
return { status: "ok", message: "Font installed" };
|
|
1403
|
+
}
|
|
1404
|
+
} catch {
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
return {
|
|
1408
|
+
status: "warn",
|
|
1409
|
+
message: "Font not found. Install: brew install --cask font-maple-mono-nf-cn"
|
|
1410
|
+
};
|
|
1411
|
+
}),
|
|
1412
|
+
// Shell
|
|
1413
|
+
check("Shell", () => ({
|
|
1414
|
+
status: "ok",
|
|
1415
|
+
message: `${os.shell} on ${os.type} (${os.arch})`
|
|
1416
|
+
})),
|
|
1417
|
+
// Git
|
|
1418
|
+
check(
|
|
1419
|
+
"Git",
|
|
1420
|
+
() => commandExists("git") ? { status: "ok", message: "Installed" } : { status: "fail", message: "Not found" }
|
|
1421
|
+
)
|
|
1422
|
+
];
|
|
1423
|
+
for (const result of checks) {
|
|
1424
|
+
console.log(formatCheck(result));
|
|
1425
|
+
}
|
|
1426
|
+
const failCount = checks.filter((c) => c.status === "fail").length;
|
|
1427
|
+
const warnCount = checks.filter((c) => c.status === "warn").length;
|
|
1428
|
+
console.log("");
|
|
1429
|
+
if (failCount > 0) {
|
|
1430
|
+
console.log(
|
|
1431
|
+
chalk5.red(
|
|
1432
|
+
` ${failCount} issue(s) need fixing. Run den init to setup.
|
|
1433
|
+
`
|
|
1434
|
+
)
|
|
1435
|
+
);
|
|
1436
|
+
} else if (warnCount > 0) {
|
|
1437
|
+
console.log(
|
|
1438
|
+
chalk5.yellow(
|
|
1439
|
+
` ${warnCount} suggestion(s). Run den init to improve your setup.
|
|
1440
|
+
`
|
|
1441
|
+
)
|
|
1442
|
+
);
|
|
1443
|
+
} else {
|
|
1444
|
+
console.log(
|
|
1445
|
+
chalk5.green(" Everything looks great! Happy coding with Claude.\n")
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// src/cli/program.ts
|
|
1452
|
+
function createProgram() {
|
|
1453
|
+
const program2 = new Command();
|
|
1454
|
+
program2.name("den").description("Your cozy terminal den for Claude Code").version("0.1.0");
|
|
1455
|
+
registerInitCommand(program2);
|
|
1456
|
+
registerThemeCommand(program2);
|
|
1457
|
+
registerLayoutCommand(program2);
|
|
1458
|
+
registerDoctorCommand(program2);
|
|
1459
|
+
registerBackupCommand(program2);
|
|
1460
|
+
registerRestoreCommand(program2);
|
|
1461
|
+
return program2;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// bin/den.ts
|
|
1465
|
+
var program = createProgram();
|
|
1466
|
+
program.parse(process.argv);
|
|
1467
|
+
//# sourceMappingURL=den.js.map
|