cc-mirror 1.0.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,4506 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/args.ts
4
+ var parseArgs = (argv) => {
5
+ const opts = { _: [], env: [] };
6
+ const args = [...argv];
7
+ while (args.length > 0) {
8
+ const arg = args.shift();
9
+ if (!arg.startsWith("-")) {
10
+ opts._.push(arg);
11
+ continue;
12
+ }
13
+ if (arg === "--yes") {
14
+ opts.yes = true;
15
+ continue;
16
+ }
17
+ if (arg === "--no-tweak") {
18
+ opts.noTweak = true;
19
+ continue;
20
+ }
21
+ if (arg === "--tui") {
22
+ opts.tui = true;
23
+ continue;
24
+ }
25
+ if (arg === "--quick" || arg === "--simple") {
26
+ opts.quick = true;
27
+ continue;
28
+ }
29
+ if (arg === "--no-tui") {
30
+ opts.noTui = true;
31
+ continue;
32
+ }
33
+ if (arg.startsWith("--env=")) {
34
+ opts.env.push(arg.slice("--env=".length));
35
+ continue;
36
+ }
37
+ if (arg === "--env") {
38
+ const value2 = args.shift();
39
+ if (value2) opts.env.push(value2);
40
+ continue;
41
+ }
42
+ const [key, inlineValue] = arg.startsWith("--") ? arg.slice(2).split("=") : [null, null];
43
+ if (!key) continue;
44
+ const value = inlineValue ?? args.shift();
45
+ if (value !== void 0) {
46
+ opts[key] = value;
47
+ } else {
48
+ opts[key] = true;
49
+ }
50
+ }
51
+ return opts;
52
+ };
53
+
54
+ // src/tui/content/haikus.ts
55
+ var COMPLETION_HAIKUS = {
56
+ zai: [
57
+ ["Gold streams, code dreams\u2014", "your variant awaits you now.", "GLM starts to gleam."],
58
+ ["Calibrated well,", "the mirror reflects your code.", "Zai hears your call."]
59
+ ],
60
+ minimax: [
61
+ ["Coral light shines bright,", "MiniMax pulses with power.", "AGI for all."],
62
+ ["Resonating deep,", "the model learns to listen.", "Your code takes its leap."]
63
+ ],
64
+ openrouter: [
65
+ ["Many paths, one door\u2014", "OpenRouter finds the way.", "Models at your core."],
66
+ ["Routes converge as one,", "your choice echoes through the wire.", "The journey begun."]
67
+ ],
68
+ ccrouter: [
69
+ ["Local models shine,", "routed through the mirror's edge.", "Your code, your design."],
70
+ ["Proxied through the night,", "your models stand at the ready.", "Code takes its first flight."]
71
+ ],
72
+ default: [
73
+ ["Mirror reflects true,", "your variant is ready now.", "Go build something new."],
74
+ ["Configuration done,", "the wrapper awaits your call.", "Your coding has begun."]
75
+ ]
76
+ };
77
+ var getRandomHaiku = (providerKey) => {
78
+ const haikus = COMPLETION_HAIKUS[providerKey || ""] || COMPLETION_HAIKUS.default;
79
+ return haikus[Math.floor(Math.random() * haikus.length)];
80
+ };
81
+
82
+ // src/cli/help.ts
83
+ var printHelp = () => {
84
+ console.log(`
85
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
86
+ \u2551 CC-MIRROR \u2551
87
+ \u2551 Create Claude Code Variants with Custom Providers \u2551
88
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
89
+
90
+ WHAT IS CC-MIRROR?
91
+ CC-MIRROR creates isolated Claude Code installations that connect to
92
+ different AI providers. Each variant is independent with its own
93
+ config, theme, and settings.
94
+
95
+ QUICK START
96
+ cc-mirror quick # Fast setup: provider + key \u2192 done
97
+ cc-mirror create # Full wizard with all options
98
+
99
+ COMMANDS
100
+ create [options] Create a new variant
101
+ quick [options] Fast: provider + API key only
102
+ list List all variants
103
+ update [name] Update to latest Claude Code
104
+ remove <name> Remove a variant
105
+ doctor Health check all variants
106
+ tweak <name> Launch tweakcc customization
107
+
108
+ OPTIONS (create/quick)
109
+ --name <name> Variant name (becomes CLI command)
110
+ --provider <name> Provider: zai | minimax | openrouter | ccrouter
111
+ --api-key <key> Provider API key
112
+ --brand <preset> Theme: auto | none | zai | minimax
113
+ --quick Fast path mode
114
+ --tui / --no-tui Force TUI on/off
115
+
116
+ OPTIONS (advanced)
117
+ --base-url <url> ANTHROPIC_BASE_URL override
118
+ --model-sonnet <name> Default Sonnet model
119
+ --model-opus <name> Default Opus model
120
+ --model-haiku <name> Default Haiku model
121
+ --root <path> Variants root (default: ~/.cc-mirror)
122
+ --bin-dir <path> Wrapper install dir (default: ~/.local/bin)
123
+ --no-tweak Skip tweakcc theming
124
+ --no-prompt-pack Skip provider prompt pack
125
+ --prompt-pack-mode <mode> minimal | maximal
126
+ --shell-env Write env vars to shell profile (Z.ai)
127
+
128
+ EXAMPLES
129
+ cc-mirror quick --provider zai
130
+ cc-mirror create --provider minimax --brand minimax
131
+ cc-mirror update zai
132
+ cc-mirror doctor
133
+
134
+ LEARN MORE
135
+ https://github.com/numman-ali/cc-mirror
136
+
137
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
138
+ Created by Numman Ali \u2022 https://x.com/nummanali
139
+ `);
140
+ };
141
+ var printHaiku = () => {
142
+ const haiku = getRandomHaiku();
143
+ console.log(`
144
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
145
+ ${haiku[0]}
146
+ ${haiku[1]}
147
+ ${haiku[2]}
148
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
149
+ `);
150
+ };
151
+
152
+ // src/cli/tui.ts
153
+ import fs from "node:fs";
154
+ import path from "node:path";
155
+ import { fileURLToPath, pathToFileURL } from "node:url";
156
+ var dirname = path.dirname(fileURLToPath(import.meta.url));
157
+ var shouldLaunchTui = (cmd, opts) => {
158
+ if (opts.noTui) return false;
159
+ if (opts.tui) return true;
160
+ if (!process.stdout.isTTY) return false;
161
+ if (cmd === "create") {
162
+ const hasArgs = opts.yes || Boolean(opts.name) || Boolean(opts.provider) || Boolean(opts["base-url"]) || Boolean(opts["api-key"]) || (opts._?.length ?? 0) > 0;
163
+ return !hasArgs;
164
+ }
165
+ return false;
166
+ };
167
+ var runTui = async () => {
168
+ const candidates = [
169
+ process.env.CC_MIRROR_TUI_PATH,
170
+ path.join(dirname, "..", "tui", "index.tsx"),
171
+ path.join(dirname, "..", "tui", "index.mjs"),
172
+ path.join(dirname, "tui.mjs")
173
+ ].filter(Boolean);
174
+ const target = candidates.find((filePath) => fs.existsSync(filePath));
175
+ if (!target) {
176
+ throw new Error("Unable to locate TUI entrypoint.");
177
+ }
178
+ await import(pathToFileURL(target).href);
179
+ };
180
+
181
+ // src/core/index.ts
182
+ import fs11 from "node:fs";
183
+ import path20 from "node:path";
184
+
185
+ // src/core/constants.ts
186
+ import os from "node:os";
187
+ import path2 from "node:path";
188
+ var DEFAULT_ROOT = path2.join(os.homedir(), ".cc-mirror");
189
+ var DEFAULT_BIN_DIR = path2.join(os.homedir(), ".local", "bin");
190
+ var TWEAKCC_VERSION = "3.2.2";
191
+ var DEFAULT_NPM_PACKAGE = "@anthropic-ai/claude-code";
192
+ var DEFAULT_NPM_VERSION = "2.0.76";
193
+
194
+ // src/core/fs.ts
195
+ import fs2 from "node:fs";
196
+ var ensureDir = (dir) => {
197
+ fs2.mkdirSync(dir, { recursive: true });
198
+ };
199
+ var writeJson = (filePath, data) => {
200
+ fs2.writeFileSync(filePath, JSON.stringify(data, null, 2));
201
+ };
202
+ var readJson = (filePath) => {
203
+ try {
204
+ return JSON.parse(fs2.readFileSync(filePath, "utf8"));
205
+ } catch {
206
+ return null;
207
+ }
208
+ };
209
+
210
+ // src/core/paths.ts
211
+ import { spawnSync } from "node:child_process";
212
+ import os2 from "node:os";
213
+ import path3 from "node:path";
214
+ var expandTilde = (input) => {
215
+ if (!input) return input;
216
+ if (input === "~") return os2.homedir();
217
+ if (input.startsWith("~/")) return path3.join(os2.homedir(), input.slice(2));
218
+ return input;
219
+ };
220
+ var commandExists = (cmd) => {
221
+ const result = spawnSync(process.platform === "win32" ? "where" : "which", [cmd], {
222
+ encoding: "utf8"
223
+ });
224
+ return result.status === 0 && result.stdout.trim().length > 0;
225
+ };
226
+
227
+ // src/core/tweakcc.ts
228
+ import fs3 from "node:fs";
229
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
230
+ import path4 from "node:path";
231
+ import { createRequire } from "node:module";
232
+
233
+ // src/brands/defaultThemes.ts
234
+ var DEFAULT_THEMES = [
235
+ {
236
+ name: "Dark mode",
237
+ id: "dark",
238
+ colors: {
239
+ autoAccept: "rgb(175,135,255)",
240
+ bashBorder: "rgb(253,93,177)",
241
+ claude: "rgb(215,119,87)",
242
+ claudeShimmer: "rgb(235,159,127)",
243
+ claudeBlue_FOR_SYSTEM_SPINNER: "rgb(147,165,255)",
244
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "rgb(177,195,255)",
245
+ permission: "rgb(177,185,249)",
246
+ permissionShimmer: "rgb(207,215,255)",
247
+ planMode: "rgb(72,150,140)",
248
+ ide: "rgb(71,130,200)",
249
+ promptBorder: "rgb(136,136,136)",
250
+ promptBorderShimmer: "rgb(166,166,166)",
251
+ text: "rgb(255,255,255)",
252
+ inverseText: "rgb(0,0,0)",
253
+ inactive: "rgb(153,153,153)",
254
+ subtle: "rgb(80,80,80)",
255
+ suggestion: "rgb(177,185,249)",
256
+ remember: "rgb(177,185,249)",
257
+ background: "rgb(0,204,204)",
258
+ success: "rgb(78,186,101)",
259
+ error: "rgb(255,107,128)",
260
+ warning: "rgb(255,193,7)",
261
+ warningShimmer: "rgb(255,223,57)",
262
+ diffAdded: "rgb(34,92,43)",
263
+ diffRemoved: "rgb(122,41,54)",
264
+ diffAddedDimmed: "rgb(71,88,74)",
265
+ diffRemovedDimmed: "rgb(105,72,77)",
266
+ diffAddedWord: "rgb(56,166,96)",
267
+ diffRemovedWord: "rgb(179,89,107)",
268
+ diffAddedWordDimmed: "rgb(46,107,58)",
269
+ diffRemovedWordDimmed: "rgb(139,57,69)",
270
+ red_FOR_SUBAGENTS_ONLY: "rgb(220,38,38)",
271
+ blue_FOR_SUBAGENTS_ONLY: "rgb(37,99,235)",
272
+ green_FOR_SUBAGENTS_ONLY: "rgb(22,163,74)",
273
+ yellow_FOR_SUBAGENTS_ONLY: "rgb(202,138,4)",
274
+ purple_FOR_SUBAGENTS_ONLY: "rgb(147,51,234)",
275
+ orange_FOR_SUBAGENTS_ONLY: "rgb(234,88,12)",
276
+ pink_FOR_SUBAGENTS_ONLY: "rgb(219,39,119)",
277
+ cyan_FOR_SUBAGENTS_ONLY: "rgb(8,145,178)",
278
+ professionalBlue: "rgb(106,155,204)",
279
+ rainbow_red: "rgb(235,95,87)",
280
+ rainbow_orange: "rgb(245,139,87)",
281
+ rainbow_yellow: "rgb(250,195,95)",
282
+ rainbow_green: "rgb(145,200,130)",
283
+ rainbow_blue: "rgb(130,170,220)",
284
+ rainbow_indigo: "rgb(155,130,200)",
285
+ rainbow_violet: "rgb(200,130,180)",
286
+ rainbow_red_shimmer: "rgb(250,155,147)",
287
+ rainbow_orange_shimmer: "rgb(255,185,137)",
288
+ rainbow_yellow_shimmer: "rgb(255,225,155)",
289
+ rainbow_green_shimmer: "rgb(185,230,180)",
290
+ rainbow_blue_shimmer: "rgb(180,205,240)",
291
+ rainbow_indigo_shimmer: "rgb(195,180,230)",
292
+ rainbow_violet_shimmer: "rgb(230,180,210)",
293
+ clawd_body: "rgb(215,119,87)",
294
+ clawd_background: "rgb(0,0,0)",
295
+ userMessageBackground: "rgb(55, 55, 55)",
296
+ bashMessageBackgroundColor: "rgb(65, 60, 65)",
297
+ memoryBackgroundColor: "rgb(55, 65, 70)",
298
+ rate_limit_fill: "rgb(177,185,249)",
299
+ rate_limit_empty: "rgb(80,83,112)"
300
+ }
301
+ },
302
+ {
303
+ name: "Light mode",
304
+ id: "light",
305
+ colors: {
306
+ autoAccept: "rgb(135,0,255)",
307
+ bashBorder: "rgb(255,0,135)",
308
+ claude: "rgb(215,119,87)",
309
+ claudeShimmer: "rgb(245,149,117)",
310
+ claudeBlue_FOR_SYSTEM_SPINNER: "rgb(87,105,247)",
311
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "rgb(117,135,255)",
312
+ permission: "rgb(87,105,247)",
313
+ permissionShimmer: "rgb(137,155,255)",
314
+ planMode: "rgb(0,102,102)",
315
+ ide: "rgb(71,130,200)",
316
+ promptBorder: "rgb(153,153,153)",
317
+ promptBorderShimmer: "rgb(183,183,183)",
318
+ text: "rgb(0,0,0)",
319
+ inverseText: "rgb(255,255,255)",
320
+ inactive: "rgb(102,102,102)",
321
+ subtle: "rgb(175,175,175)",
322
+ suggestion: "rgb(87,105,247)",
323
+ remember: "rgb(0,0,255)",
324
+ background: "rgb(0,153,153)",
325
+ success: "rgb(44,122,57)",
326
+ error: "rgb(171,43,63)",
327
+ warning: "rgb(150,108,30)",
328
+ warningShimmer: "rgb(200,158,80)",
329
+ diffAdded: "rgb(105,219,124)",
330
+ diffRemoved: "rgb(255,168,180)",
331
+ diffAddedDimmed: "rgb(199,225,203)",
332
+ diffRemovedDimmed: "rgb(253,210,216)",
333
+ diffAddedWord: "rgb(47,157,68)",
334
+ diffRemovedWord: "rgb(209,69,75)",
335
+ diffAddedWordDimmed: "rgb(144,194,156)",
336
+ diffRemovedWordDimmed: "rgb(232,165,173)",
337
+ red_FOR_SUBAGENTS_ONLY: "rgb(220,38,38)",
338
+ blue_FOR_SUBAGENTS_ONLY: "rgb(37,99,235)",
339
+ green_FOR_SUBAGENTS_ONLY: "rgb(22,163,74)",
340
+ yellow_FOR_SUBAGENTS_ONLY: "rgb(202,138,4)",
341
+ purple_FOR_SUBAGENTS_ONLY: "rgb(147,51,234)",
342
+ orange_FOR_SUBAGENTS_ONLY: "rgb(234,88,12)",
343
+ pink_FOR_SUBAGENTS_ONLY: "rgb(219,39,119)",
344
+ cyan_FOR_SUBAGENTS_ONLY: "rgb(8,145,178)",
345
+ professionalBlue: "rgb(106,155,204)",
346
+ rainbow_red: "rgb(235,95,87)",
347
+ rainbow_orange: "rgb(245,139,87)",
348
+ rainbow_yellow: "rgb(250,195,95)",
349
+ rainbow_green: "rgb(145,200,130)",
350
+ rainbow_blue: "rgb(130,170,220)",
351
+ rainbow_indigo: "rgb(155,130,200)",
352
+ rainbow_violet: "rgb(200,130,180)",
353
+ rainbow_red_shimmer: "rgb(250,155,147)",
354
+ rainbow_orange_shimmer: "rgb(255,185,137)",
355
+ rainbow_yellow_shimmer: "rgb(255,225,155)",
356
+ rainbow_green_shimmer: "rgb(185,230,180)",
357
+ rainbow_blue_shimmer: "rgb(180,205,240)",
358
+ rainbow_indigo_shimmer: "rgb(195,180,230)",
359
+ rainbow_violet_shimmer: "rgb(230,180,210)",
360
+ clawd_body: "rgb(215,119,87)",
361
+ clawd_background: "rgb(0,0,0)",
362
+ userMessageBackground: "rgb(240, 240, 240)",
363
+ bashMessageBackgroundColor: "rgb(250, 245, 250)",
364
+ memoryBackgroundColor: "rgb(230, 245, 250)",
365
+ rate_limit_fill: "rgb(87,105,247)",
366
+ rate_limit_empty: "rgb(39,47,111)"
367
+ }
368
+ },
369
+ {
370
+ name: "Light mode (ANSI colors only)",
371
+ id: "light-ansi",
372
+ colors: {
373
+ autoAccept: "ansi:magenta",
374
+ bashBorder: "ansi:magenta",
375
+ claude: "ansi:redBright",
376
+ claudeShimmer: "ansi:yellowBright",
377
+ claudeBlue_FOR_SYSTEM_SPINNER: "ansi:blue",
378
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "ansi:blueBright",
379
+ permission: "ansi:blue",
380
+ permissionShimmer: "ansi:blueBright",
381
+ planMode: "ansi:cyan",
382
+ ide: "ansi:blueBright",
383
+ promptBorder: "ansi:white",
384
+ promptBorderShimmer: "ansi:whiteBright",
385
+ text: "ansi:black",
386
+ inverseText: "ansi:white",
387
+ inactive: "ansi:blackBright",
388
+ subtle: "ansi:blackBright",
389
+ suggestion: "ansi:blue",
390
+ remember: "ansi:blue",
391
+ background: "ansi:cyan",
392
+ success: "ansi:green",
393
+ error: "ansi:red",
394
+ warning: "ansi:yellow",
395
+ warningShimmer: "ansi:yellowBright",
396
+ diffAdded: "ansi:green",
397
+ diffRemoved: "ansi:red",
398
+ diffAddedDimmed: "ansi:green",
399
+ diffRemovedDimmed: "ansi:red",
400
+ diffAddedWord: "ansi:greenBright",
401
+ diffRemovedWord: "ansi:redBright",
402
+ diffAddedWordDimmed: "ansi:green",
403
+ diffRemovedWordDimmed: "ansi:red",
404
+ red_FOR_SUBAGENTS_ONLY: "ansi:red",
405
+ blue_FOR_SUBAGENTS_ONLY: "ansi:blue",
406
+ green_FOR_SUBAGENTS_ONLY: "ansi:green",
407
+ yellow_FOR_SUBAGENTS_ONLY: "ansi:yellow",
408
+ purple_FOR_SUBAGENTS_ONLY: "ansi:magenta",
409
+ orange_FOR_SUBAGENTS_ONLY: "ansi:redBright",
410
+ pink_FOR_SUBAGENTS_ONLY: "ansi:magentaBright",
411
+ cyan_FOR_SUBAGENTS_ONLY: "ansi:cyan",
412
+ professionalBlue: "ansi:blueBright",
413
+ rainbow_red: "ansi:red",
414
+ rainbow_orange: "ansi:redBright",
415
+ rainbow_yellow: "ansi:yellow",
416
+ rainbow_green: "ansi:green",
417
+ rainbow_blue: "ansi:cyan",
418
+ rainbow_indigo: "ansi:blue",
419
+ rainbow_violet: "ansi:magenta",
420
+ rainbow_red_shimmer: "ansi:redBright",
421
+ rainbow_orange_shimmer: "ansi:yellow",
422
+ rainbow_yellow_shimmer: "ansi:yellowBright",
423
+ rainbow_green_shimmer: "ansi:greenBright",
424
+ rainbow_blue_shimmer: "ansi:cyanBright",
425
+ rainbow_indigo_shimmer: "ansi:blueBright",
426
+ rainbow_violet_shimmer: "ansi:magentaBright",
427
+ clawd_body: "ansi:redBright",
428
+ clawd_background: "ansi:black",
429
+ userMessageBackground: "ansi:white",
430
+ bashMessageBackgroundColor: "ansi:whiteBright",
431
+ memoryBackgroundColor: "ansi:white",
432
+ rate_limit_fill: "ansi:yellow",
433
+ rate_limit_empty: "ansi:black"
434
+ }
435
+ },
436
+ {
437
+ name: "Dark mode (ANSI colors only)",
438
+ id: "dark-ansi",
439
+ colors: {
440
+ autoAccept: "ansi:magentaBright",
441
+ bashBorder: "ansi:magentaBright",
442
+ claude: "ansi:redBright",
443
+ claudeShimmer: "ansi:yellowBright",
444
+ claudeBlue_FOR_SYSTEM_SPINNER: "ansi:blueBright",
445
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "ansi:blueBright",
446
+ permission: "ansi:blueBright",
447
+ permissionShimmer: "ansi:blueBright",
448
+ planMode: "ansi:cyanBright",
449
+ ide: "ansi:blue",
450
+ promptBorder: "ansi:white",
451
+ promptBorderShimmer: "ansi:whiteBright",
452
+ text: "ansi:whiteBright",
453
+ inverseText: "ansi:black",
454
+ inactive: "ansi:white",
455
+ subtle: "ansi:white",
456
+ suggestion: "ansi:blueBright",
457
+ remember: "ansi:blueBright",
458
+ background: "ansi:cyanBright",
459
+ success: "ansi:greenBright",
460
+ error: "ansi:redBright",
461
+ warning: "ansi:yellowBright",
462
+ warningShimmer: "ansi:yellowBright",
463
+ diffAdded: "ansi:green",
464
+ diffRemoved: "ansi:red",
465
+ diffAddedDimmed: "ansi:green",
466
+ diffRemovedDimmed: "ansi:red",
467
+ diffAddedWord: "ansi:greenBright",
468
+ diffRemovedWord: "ansi:redBright",
469
+ diffAddedWordDimmed: "ansi:green",
470
+ diffRemovedWordDimmed: "ansi:red",
471
+ red_FOR_SUBAGENTS_ONLY: "ansi:redBright",
472
+ blue_FOR_SUBAGENTS_ONLY: "ansi:blueBright",
473
+ green_FOR_SUBAGENTS_ONLY: "ansi:greenBright",
474
+ yellow_FOR_SUBAGENTS_ONLY: "ansi:yellowBright",
475
+ purple_FOR_SUBAGENTS_ONLY: "ansi:magentaBright",
476
+ orange_FOR_SUBAGENTS_ONLY: "ansi:redBright",
477
+ pink_FOR_SUBAGENTS_ONLY: "ansi:magentaBright",
478
+ cyan_FOR_SUBAGENTS_ONLY: "ansi:cyanBright",
479
+ professionalBlue: "rgb(106,155,204)",
480
+ rainbow_red: "ansi:red",
481
+ rainbow_orange: "ansi:redBright",
482
+ rainbow_yellow: "ansi:yellow",
483
+ rainbow_green: "ansi:green",
484
+ rainbow_blue: "ansi:cyan",
485
+ rainbow_indigo: "ansi:blue",
486
+ rainbow_violet: "ansi:magenta",
487
+ rainbow_red_shimmer: "ansi:redBright",
488
+ rainbow_orange_shimmer: "ansi:yellow",
489
+ rainbow_yellow_shimmer: "ansi:yellowBright",
490
+ rainbow_green_shimmer: "ansi:greenBright",
491
+ rainbow_blue_shimmer: "ansi:cyanBright",
492
+ rainbow_indigo_shimmer: "ansi:blueBright",
493
+ rainbow_violet_shimmer: "ansi:magentaBright",
494
+ clawd_body: "ansi:redBright",
495
+ clawd_background: "ansi:black",
496
+ userMessageBackground: "ansi:blackBright",
497
+ bashMessageBackgroundColor: "ansi:black",
498
+ memoryBackgroundColor: "ansi:blackBright",
499
+ rate_limit_fill: "ansi:yellow",
500
+ rate_limit_empty: "ansi:white"
501
+ }
502
+ },
503
+ {
504
+ name: "Light mode (colorblind-friendly)",
505
+ id: "light-daltonized",
506
+ colors: {
507
+ autoAccept: "rgb(135,0,255)",
508
+ bashBorder: "rgb(0,102,204)",
509
+ claude: "rgb(255,153,51)",
510
+ claudeShimmer: "rgb(255,183,101)",
511
+ claudeBlue_FOR_SYSTEM_SPINNER: "rgb(51,102,255)",
512
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "rgb(101,152,255)",
513
+ permission: "rgb(51,102,255)",
514
+ permissionShimmer: "rgb(101,152,255)",
515
+ planMode: "rgb(51,102,102)",
516
+ ide: "rgb(71,130,200)",
517
+ promptBorder: "rgb(153,153,153)",
518
+ promptBorderShimmer: "rgb(183,183,183)",
519
+ text: "rgb(0,0,0)",
520
+ inverseText: "rgb(255,255,255)",
521
+ inactive: "rgb(102,102,102)",
522
+ subtle: "rgb(175,175,175)",
523
+ suggestion: "rgb(51,102,255)",
524
+ remember: "rgb(51,102,255)",
525
+ background: "rgb(0,153,153)",
526
+ success: "rgb(0,102,153)",
527
+ error: "rgb(204,0,0)",
528
+ warning: "rgb(255,153,0)",
529
+ warningShimmer: "rgb(255,183,50)",
530
+ diffAdded: "rgb(153,204,255)",
531
+ diffRemoved: "rgb(255,204,204)",
532
+ diffAddedDimmed: "rgb(209,231,253)",
533
+ diffRemovedDimmed: "rgb(255,233,233)",
534
+ diffAddedWord: "rgb(51,102,204)",
535
+ diffRemovedWord: "rgb(153,51,51)",
536
+ diffAddedWordDimmed: "rgb(102,153,204)",
537
+ diffRemovedWordDimmed: "rgb(204,153,153)",
538
+ red_FOR_SUBAGENTS_ONLY: "rgb(204,0,0)",
539
+ blue_FOR_SUBAGENTS_ONLY: "rgb(0,102,204)",
540
+ green_FOR_SUBAGENTS_ONLY: "rgb(0,204,0)",
541
+ yellow_FOR_SUBAGENTS_ONLY: "rgb(255,204,0)",
542
+ purple_FOR_SUBAGENTS_ONLY: "rgb(128,0,128)",
543
+ orange_FOR_SUBAGENTS_ONLY: "rgb(255,128,0)",
544
+ pink_FOR_SUBAGENTS_ONLY: "rgb(255,102,178)",
545
+ cyan_FOR_SUBAGENTS_ONLY: "rgb(0,178,178)",
546
+ professionalBlue: "rgb(106,155,204)",
547
+ rainbow_red: "rgb(235,95,87)",
548
+ rainbow_orange: "rgb(245,139,87)",
549
+ rainbow_yellow: "rgb(250,195,95)",
550
+ rainbow_green: "rgb(145,200,130)",
551
+ rainbow_blue: "rgb(130,170,220)",
552
+ rainbow_indigo: "rgb(155,130,200)",
553
+ rainbow_violet: "rgb(200,130,180)",
554
+ rainbow_red_shimmer: "rgb(250,155,147)",
555
+ rainbow_orange_shimmer: "rgb(255,185,137)",
556
+ rainbow_yellow_shimmer: "rgb(255,225,155)",
557
+ rainbow_green_shimmer: "rgb(185,230,180)",
558
+ rainbow_blue_shimmer: "rgb(180,205,240)",
559
+ rainbow_indigo_shimmer: "rgb(195,180,230)",
560
+ rainbow_violet_shimmer: "rgb(230,180,210)",
561
+ clawd_body: "rgb(215,119,87)",
562
+ clawd_background: "rgb(0,0,0)",
563
+ userMessageBackground: "rgb(220, 220, 220)",
564
+ bashMessageBackgroundColor: "rgb(250, 245, 250)",
565
+ memoryBackgroundColor: "rgb(230, 245, 250)",
566
+ rate_limit_fill: "rgb(51,102,255)",
567
+ rate_limit_empty: "rgb(23,46,114)"
568
+ }
569
+ },
570
+ {
571
+ name: "Dark mode (colorblind-friendly)",
572
+ id: "dark-daltonized",
573
+ colors: {
574
+ autoAccept: "rgb(175,135,255)",
575
+ bashBorder: "rgb(51,153,255)",
576
+ claude: "rgb(255,153,51)",
577
+ claudeShimmer: "rgb(255,183,101)",
578
+ claudeBlue_FOR_SYSTEM_SPINNER: "rgb(153,204,255)",
579
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "rgb(183,224,255)",
580
+ permission: "rgb(153,204,255)",
581
+ permissionShimmer: "rgb(183,224,255)",
582
+ planMode: "rgb(102,153,153)",
583
+ ide: "rgb(71,130,200)",
584
+ promptBorder: "rgb(136,136,136)",
585
+ promptBorderShimmer: "rgb(166,166,166)",
586
+ text: "rgb(255,255,255)",
587
+ inverseText: "rgb(0,0,0)",
588
+ inactive: "rgb(153,153,153)",
589
+ subtle: "rgb(80,80,80)",
590
+ suggestion: "rgb(153,204,255)",
591
+ remember: "rgb(153,204,255)",
592
+ background: "rgb(0,204,204)",
593
+ success: "rgb(51,153,255)",
594
+ error: "rgb(255,102,102)",
595
+ warning: "rgb(255,204,0)",
596
+ warningShimmer: "rgb(255,234,50)",
597
+ diffAdded: "rgb(0,68,102)",
598
+ diffRemoved: "rgb(102,0,0)",
599
+ diffAddedDimmed: "rgb(62,81,91)",
600
+ diffRemovedDimmed: "rgb(62,44,44)",
601
+ diffAddedWord: "rgb(0,119,179)",
602
+ diffRemovedWord: "rgb(179,0,0)",
603
+ diffAddedWordDimmed: "rgb(26,99,128)",
604
+ diffRemovedWordDimmed: "rgb(128,21,21)",
605
+ red_FOR_SUBAGENTS_ONLY: "rgb(255,102,102)",
606
+ blue_FOR_SUBAGENTS_ONLY: "rgb(102,178,255)",
607
+ green_FOR_SUBAGENTS_ONLY: "rgb(102,255,102)",
608
+ yellow_FOR_SUBAGENTS_ONLY: "rgb(255,255,102)",
609
+ purple_FOR_SUBAGENTS_ONLY: "rgb(178,102,255)",
610
+ orange_FOR_SUBAGENTS_ONLY: "rgb(255,178,102)",
611
+ pink_FOR_SUBAGENTS_ONLY: "rgb(255,153,204)",
612
+ cyan_FOR_SUBAGENTS_ONLY: "rgb(102,204,204)",
613
+ professionalBlue: "rgb(106,155,204)",
614
+ rainbow_red: "rgb(235,95,87)",
615
+ rainbow_orange: "rgb(245,139,87)",
616
+ rainbow_yellow: "rgb(250,195,95)",
617
+ rainbow_green: "rgb(145,200,130)",
618
+ rainbow_blue: "rgb(130,170,220)",
619
+ rainbow_indigo: "rgb(155,130,200)",
620
+ rainbow_violet: "rgb(200,130,180)",
621
+ rainbow_red_shimmer: "rgb(250,155,147)",
622
+ rainbow_orange_shimmer: "rgb(255,185,137)",
623
+ rainbow_yellow_shimmer: "rgb(255,225,155)",
624
+ rainbow_green_shimmer: "rgb(185,230,180)",
625
+ rainbow_blue_shimmer: "rgb(180,205,240)",
626
+ rainbow_indigo_shimmer: "rgb(195,180,230)",
627
+ rainbow_violet_shimmer: "rgb(230,180,210)",
628
+ clawd_body: "rgb(215,119,87)",
629
+ clawd_background: "rgb(0,0,0)",
630
+ userMessageBackground: "rgb(55, 55, 55)",
631
+ bashMessageBackgroundColor: "rgb(65, 60, 65)",
632
+ memoryBackgroundColor: "rgb(55, 65, 70)",
633
+ rate_limit_fill: "rgb(153,204,255)",
634
+ rate_limit_empty: "rgb(69,92,115)"
635
+ }
636
+ },
637
+ {
638
+ name: "Monochrome",
639
+ id: "monochrome",
640
+ colors: {
641
+ autoAccept: "rgb(200,200,200)",
642
+ bashBorder: "rgb(180,180,180)",
643
+ claude: "rgb(255,255,255)",
644
+ claudeShimmer: "rgb(230,230,230)",
645
+ claudeBlue_FOR_SYSTEM_SPINNER: "rgb(200,200,200)",
646
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: "rgb(220,220,220)",
647
+ permission: "rgb(200,200,200)",
648
+ permissionShimmer: "rgb(220,220,220)",
649
+ planMode: "rgb(180,180,180)",
650
+ ide: "rgb(190,190,190)",
651
+ promptBorder: "rgb(160,160,160)",
652
+ promptBorderShimmer: "rgb(180,180,180)",
653
+ text: "rgb(255,255,255)",
654
+ inverseText: "rgb(40,40,40)",
655
+ inactive: "rgb(120,120,120)",
656
+ subtle: "rgb(100,100,100)",
657
+ suggestion: "rgb(200,200,200)",
658
+ remember: "rgb(200,200,200)",
659
+ background: "rgb(180,180,180)",
660
+ success: "rgb(220,220,220)",
661
+ error: "rgb(180,180,180)",
662
+ warning: "rgb(200,200,200)",
663
+ warningShimmer: "rgb(220,220,220)",
664
+ diffAdded: "rgb(90,90,90)",
665
+ diffRemoved: "rgb(60,60,60)",
666
+ diffAddedDimmed: "rgb(110,110,110)",
667
+ diffRemovedDimmed: "rgb(80,80,80)",
668
+ diffAddedWord: "rgb(200,200,200)",
669
+ diffRemovedWord: "rgb(80,80,80)",
670
+ diffAddedWordDimmed: "rgb(160,160,160)",
671
+ diffRemovedWordDimmed: "rgb(70,70,70)",
672
+ red_FOR_SUBAGENTS_ONLY: "rgb(200,200,200)",
673
+ blue_FOR_SUBAGENTS_ONLY: "rgb(180,180,180)",
674
+ green_FOR_SUBAGENTS_ONLY: "rgb(210,210,210)",
675
+ yellow_FOR_SUBAGENTS_ONLY: "rgb(190,190,190)",
676
+ purple_FOR_SUBAGENTS_ONLY: "rgb(170,170,170)",
677
+ orange_FOR_SUBAGENTS_ONLY: "rgb(195,195,195)",
678
+ pink_FOR_SUBAGENTS_ONLY: "rgb(205,205,205)",
679
+ cyan_FOR_SUBAGENTS_ONLY: "rgb(185,185,185)",
680
+ professionalBlue: "rgb(190,190,190)",
681
+ rainbow_red: "rgb(240,240,240)",
682
+ rainbow_orange: "rgb(230,230,230)",
683
+ rainbow_yellow: "rgb(220,220,220)",
684
+ rainbow_green: "rgb(210,210,210)",
685
+ rainbow_blue: "rgb(200,200,200)",
686
+ rainbow_indigo: "rgb(190,190,190)",
687
+ rainbow_violet: "rgb(180,180,180)",
688
+ rainbow_red_shimmer: "rgb(255,255,255)",
689
+ rainbow_orange_shimmer: "rgb(245,245,245)",
690
+ rainbow_yellow_shimmer: "rgb(235,235,235)",
691
+ rainbow_green_shimmer: "rgb(225,225,225)",
692
+ rainbow_blue_shimmer: "rgb(215,215,215)",
693
+ rainbow_indigo_shimmer: "rgb(205,205,205)",
694
+ rainbow_violet_shimmer: "rgb(195,195,195)",
695
+ clawd_body: "rgb(255,255,255)",
696
+ clawd_background: "rgb(40,40,40)",
697
+ userMessageBackground: "rgb(70,70,70)",
698
+ bashMessageBackgroundColor: "rgb(65,65,65)",
699
+ memoryBackgroundColor: "rgb(75,75,75)",
700
+ rate_limit_fill: "rgb(200,200,200)",
701
+ rate_limit_empty: "rgb(90,90,90)"
702
+ }
703
+ }
704
+ ];
705
+
706
+ // src/brands/userLabel.ts
707
+ import os3 from "node:os";
708
+ var sanitizeLabel = (value) => {
709
+ if (!value) return null;
710
+ const trimmed = value.trim();
711
+ if (!trimmed) return null;
712
+ return trimmed;
713
+ };
714
+ var getUserLabel = () => {
715
+ const candidates = [
716
+ sanitizeLabel(process.env.CLAUDE_CODE_USER_LABEL),
717
+ sanitizeLabel(process.env.USER),
718
+ sanitizeLabel(process.env.USERNAME)
719
+ ];
720
+ for (const candidate of candidates) {
721
+ if (candidate) return candidate;
722
+ }
723
+ try {
724
+ const info = os3.userInfo();
725
+ const name = sanitizeLabel(info.username);
726
+ if (name) return name;
727
+ } catch {
728
+ }
729
+ return "user";
730
+ };
731
+ var formatUserMessage = (label) => ` [${label}] {} `;
732
+
733
+ // src/brands/zai.ts
734
+ var clamp = (value) => Math.max(0, Math.min(255, Math.round(value)));
735
+ var hexToRgb = (hex) => {
736
+ const normalized = hex.replace("#", "").trim();
737
+ if (normalized.length === 3) {
738
+ const [r, g, b] = normalized.split("");
739
+ return {
740
+ r: clamp(parseInt(r + r, 16)),
741
+ g: clamp(parseInt(g + g, 16)),
742
+ b: clamp(parseInt(b + b, 16))
743
+ };
744
+ }
745
+ if (normalized.length !== 6) {
746
+ throw new Error(`Unsupported hex color: ${hex}`);
747
+ }
748
+ return {
749
+ r: clamp(parseInt(normalized.slice(0, 2), 16)),
750
+ g: clamp(parseInt(normalized.slice(2, 4), 16)),
751
+ b: clamp(parseInt(normalized.slice(4, 6), 16))
752
+ };
753
+ };
754
+ var rgb = (hex) => {
755
+ const { r, g, b } = hexToRgb(hex);
756
+ return `rgb(${r},${g},${b})`;
757
+ };
758
+ var mix = (hexA, hexB, weight) => {
759
+ const a = hexToRgb(hexA);
760
+ const b = hexToRgb(hexB);
761
+ const w = Math.max(0, Math.min(1, weight));
762
+ return `rgb(${clamp(a.r + (b.r - a.r) * w)},${clamp(a.g + (b.g - a.g) * w)},${clamp(a.b + (b.b - a.b) * w)})`;
763
+ };
764
+ var lighten = (hex, weight) => mix(hex, "#ffffff", weight);
765
+ var palette = {
766
+ base: "#1b1d1f",
767
+ surface: "#25272b",
768
+ panel: "#282a30",
769
+ border: "#353842",
770
+ borderStrong: "#484a58",
771
+ text: "#e8e8e8",
772
+ textMuted: "#c3c4cc",
773
+ textDim: "#8d8e99",
774
+ gold: "#ffd373",
775
+ goldSoft: "#ffdc8f",
776
+ goldDeep: "#b2892e",
777
+ blue: "#0080ff",
778
+ blueSoft: "#66b3ff",
779
+ blueDeep: "#134cff",
780
+ green: "#37d995",
781
+ red: "#fb2c36",
782
+ orange: "#ff8e42",
783
+ purple: "#9c8bff"
784
+ };
785
+ var theme = {
786
+ name: "Z.ai Carbon",
787
+ id: "zai-carbon",
788
+ colors: {
789
+ autoAccept: rgb(palette.green),
790
+ bashBorder: rgb(palette.gold),
791
+ claude: rgb(palette.gold),
792
+ claudeShimmer: rgb(palette.goldSoft),
793
+ claudeBlue_FOR_SYSTEM_SPINNER: rgb(palette.blue),
794
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: rgb(palette.blueSoft),
795
+ permission: rgb(palette.blue),
796
+ permissionShimmer: rgb(palette.blueSoft),
797
+ planMode: rgb(palette.green),
798
+ ide: rgb(palette.blueSoft),
799
+ promptBorder: rgb(palette.border),
800
+ promptBorderShimmer: rgb(palette.borderStrong),
801
+ text: rgb(palette.text),
802
+ inverseText: rgb(palette.base),
803
+ inactive: rgb(palette.textDim),
804
+ subtle: rgb(palette.border),
805
+ suggestion: rgb(palette.blueSoft),
806
+ remember: rgb(palette.gold),
807
+ background: rgb(palette.base),
808
+ success: rgb(palette.green),
809
+ error: rgb(palette.red),
810
+ warning: rgb(palette.orange),
811
+ warningShimmer: rgb(palette.goldSoft),
812
+ diffAdded: mix(palette.base, palette.green, 0.18),
813
+ diffRemoved: mix(palette.base, palette.red, 0.18),
814
+ diffAddedDimmed: mix(palette.base, palette.green, 0.1),
815
+ diffRemovedDimmed: mix(palette.base, palette.red, 0.1),
816
+ diffAddedWord: mix(palette.base, palette.green, 0.45),
817
+ diffRemovedWord: mix(palette.base, palette.red, 0.45),
818
+ diffAddedWordDimmed: mix(palette.base, palette.green, 0.3),
819
+ diffRemovedWordDimmed: mix(palette.base, palette.red, 0.3),
820
+ red_FOR_SUBAGENTS_ONLY: rgb(palette.red),
821
+ blue_FOR_SUBAGENTS_ONLY: rgb(palette.blueDeep),
822
+ green_FOR_SUBAGENTS_ONLY: rgb(palette.green),
823
+ yellow_FOR_SUBAGENTS_ONLY: rgb(palette.gold),
824
+ purple_FOR_SUBAGENTS_ONLY: rgb(palette.purple),
825
+ orange_FOR_SUBAGENTS_ONLY: rgb(palette.orange),
826
+ pink_FOR_SUBAGENTS_ONLY: rgb(palette.goldSoft),
827
+ cyan_FOR_SUBAGENTS_ONLY: rgb(palette.blueSoft),
828
+ professionalBlue: rgb(palette.blueSoft),
829
+ rainbow_red: rgb(palette.red),
830
+ rainbow_orange: rgb(palette.orange),
831
+ rainbow_yellow: rgb(palette.gold),
832
+ rainbow_green: rgb(palette.green),
833
+ rainbow_blue: rgb(palette.blue),
834
+ rainbow_indigo: rgb(palette.blueDeep),
835
+ rainbow_violet: rgb(palette.purple),
836
+ rainbow_red_shimmer: lighten(palette.red, 0.35),
837
+ rainbow_orange_shimmer: lighten(palette.orange, 0.35),
838
+ rainbow_yellow_shimmer: lighten(palette.gold, 0.25),
839
+ rainbow_green_shimmer: lighten(palette.green, 0.35),
840
+ rainbow_blue_shimmer: lighten(palette.blue, 0.35),
841
+ rainbow_indigo_shimmer: lighten(palette.blueDeep, 0.35),
842
+ rainbow_violet_shimmer: lighten(palette.purple, 0.35),
843
+ clawd_body: rgb(palette.gold),
844
+ clawd_background: rgb(palette.base),
845
+ userMessageBackground: rgb(palette.panel),
846
+ bashMessageBackgroundColor: rgb(palette.surface),
847
+ memoryBackgroundColor: rgb(palette.panel),
848
+ rate_limit_fill: rgb(palette.gold),
849
+ rate_limit_empty: rgb(palette.borderStrong)
850
+ }
851
+ };
852
+ var buildZaiTweakccConfig = () => ({
853
+ ccVersion: "",
854
+ ccInstallationPath: null,
855
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
856
+ changesApplied: false,
857
+ hidePiebaldAnnouncement: true,
858
+ settings: {
859
+ themes: [theme, ...DEFAULT_THEMES],
860
+ thinkingVerbs: {
861
+ format: "{}... ",
862
+ verbs: [
863
+ "Calibrating",
864
+ "Indexing",
865
+ "Synthesizing",
866
+ "Optimizing",
867
+ "Routing",
868
+ "Vectorizing",
869
+ "Mapping",
870
+ "Compiling",
871
+ "Refining",
872
+ "Auditing",
873
+ "Aligning",
874
+ "Balancing",
875
+ "Forecasting",
876
+ "Resolving",
877
+ "Validating",
878
+ "Benchmarking",
879
+ "Assembling",
880
+ "Delivering"
881
+ ]
882
+ },
883
+ thinkingStyle: {
884
+ updateInterval: 110,
885
+ phases: [".", "o", "O", "0", "O", "o"],
886
+ reverseMirror: false
887
+ },
888
+ userMessageDisplay: {
889
+ format: formatUserMessage(getUserLabel()),
890
+ styling: ["bold"],
891
+ foregroundColor: "default",
892
+ backgroundColor: "default",
893
+ borderStyle: "topBottomBold",
894
+ borderColor: rgb(palette.gold),
895
+ paddingX: 1,
896
+ paddingY: 0,
897
+ fitBoxToContent: true
898
+ },
899
+ inputBox: {
900
+ removeBorder: true
901
+ },
902
+ misc: {
903
+ showTweakccVersion: false,
904
+ showPatchesApplied: false,
905
+ expandThinkingBlocks: true,
906
+ enableConversationTitle: true,
907
+ hideStartupBanner: true,
908
+ hideCtrlGToEditPrompt: true,
909
+ hideStartupClawd: true,
910
+ increaseFileReadLimit: true
911
+ },
912
+ toolsets: [
913
+ {
914
+ name: "zai",
915
+ allowedTools: "*"
916
+ }
917
+ ],
918
+ defaultToolset: "zai",
919
+ planModeToolset: "zai"
920
+ }
921
+ });
922
+
923
+ // src/brands/minimax.ts
924
+ var clamp2 = (value) => Math.max(0, Math.min(255, Math.round(value)));
925
+ var hexToRgb2 = (hex) => {
926
+ const normalized = hex.replace("#", "").trim();
927
+ if (normalized.length === 3) {
928
+ const [r, g, b] = normalized.split("");
929
+ return {
930
+ r: clamp2(parseInt(r + r, 16)),
931
+ g: clamp2(parseInt(g + g, 16)),
932
+ b: clamp2(parseInt(b + b, 16))
933
+ };
934
+ }
935
+ if (normalized.length !== 6) {
936
+ throw new Error(`Unsupported hex color: ${hex}`);
937
+ }
938
+ return {
939
+ r: clamp2(parseInt(normalized.slice(0, 2), 16)),
940
+ g: clamp2(parseInt(normalized.slice(2, 4), 16)),
941
+ b: clamp2(parseInt(normalized.slice(4, 6), 16))
942
+ };
943
+ };
944
+ var rgb2 = (hex) => {
945
+ const { r, g, b } = hexToRgb2(hex);
946
+ return `rgb(${r},${g},${b})`;
947
+ };
948
+ var mix2 = (hexA, hexB, weight) => {
949
+ const a = hexToRgb2(hexA);
950
+ const b = hexToRgb2(hexB);
951
+ const w = Math.max(0, Math.min(1, weight));
952
+ return `rgb(${clamp2(a.r + (b.r - a.r) * w)},${clamp2(a.g + (b.g - a.g) * w)},${clamp2(a.b + (b.b - a.b) * w)})`;
953
+ };
954
+ var lighten2 = (hex, weight) => mix2(hex, "#ffffff", weight);
955
+ var MINIMAX_CORE = "#ff5733";
956
+ var palette2 = {
957
+ base: "#0b0b0f",
958
+ surface: "#15151d",
959
+ panel: "#1b1b24",
960
+ border: "#2b2b36",
961
+ borderStrong: "#3a3a4a",
962
+ text: "#f7f7fb",
963
+ textMuted: "#d1d1db",
964
+ textDim: "#9b9bac",
965
+ core: MINIMAX_CORE,
966
+ deep: "#c70039",
967
+ orange: "#ff8c42",
968
+ yellow: "#ffeb3b",
969
+ cyan: "#4db5ff",
970
+ blue: "#3a7dff",
971
+ green: "#22c55e",
972
+ red: "#ff3b3b"
973
+ };
974
+ var makeTheme = () => {
975
+ const tint = (hex, weight) => mix2(palette2.base, hex, weight);
976
+ return {
977
+ name: "MiniMax Pulse",
978
+ id: "minimax-pulse",
979
+ colors: {
980
+ autoAccept: rgb2(palette2.green),
981
+ bashBorder: rgb2(palette2.core),
982
+ claude: rgb2(palette2.core),
983
+ claudeShimmer: lighten2(palette2.core, 0.28),
984
+ claudeBlue_FOR_SYSTEM_SPINNER: rgb2(palette2.blue),
985
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: lighten2(palette2.blue, 0.3),
986
+ permission: rgb2(palette2.orange),
987
+ permissionShimmer: lighten2(palette2.orange, 0.25),
988
+ planMode: rgb2(palette2.core),
989
+ ide: rgb2(palette2.cyan),
990
+ promptBorder: rgb2(palette2.border),
991
+ promptBorderShimmer: rgb2(palette2.borderStrong),
992
+ text: rgb2(palette2.text),
993
+ inverseText: rgb2(palette2.base),
994
+ inactive: rgb2(palette2.textDim),
995
+ subtle: tint(palette2.core, 0.18),
996
+ suggestion: rgb2(palette2.deep),
997
+ remember: rgb2(palette2.core),
998
+ background: rgb2(palette2.base),
999
+ success: rgb2(palette2.green),
1000
+ error: rgb2(palette2.red),
1001
+ warning: rgb2(palette2.yellow),
1002
+ warningShimmer: lighten2(palette2.yellow, 0.2),
1003
+ diffAdded: mix2(palette2.base, palette2.green, 0.18),
1004
+ diffRemoved: mix2(palette2.base, palette2.red, 0.18),
1005
+ diffAddedDimmed: mix2(palette2.base, palette2.green, 0.1),
1006
+ diffRemovedDimmed: mix2(palette2.base, palette2.red, 0.1),
1007
+ diffAddedWord: mix2(palette2.base, palette2.green, 0.45),
1008
+ diffRemovedWord: mix2(palette2.base, palette2.red, 0.45),
1009
+ diffAddedWordDimmed: mix2(palette2.base, palette2.green, 0.3),
1010
+ diffRemovedWordDimmed: mix2(palette2.base, palette2.red, 0.3),
1011
+ red_FOR_SUBAGENTS_ONLY: rgb2(palette2.red),
1012
+ blue_FOR_SUBAGENTS_ONLY: rgb2(palette2.blue),
1013
+ green_FOR_SUBAGENTS_ONLY: rgb2(palette2.green),
1014
+ yellow_FOR_SUBAGENTS_ONLY: rgb2(palette2.yellow),
1015
+ purple_FOR_SUBAGENTS_ONLY: rgb2(palette2.deep),
1016
+ orange_FOR_SUBAGENTS_ONLY: rgb2(palette2.orange),
1017
+ pink_FOR_SUBAGENTS_ONLY: rgb2(palette2.core),
1018
+ cyan_FOR_SUBAGENTS_ONLY: rgb2(palette2.cyan),
1019
+ professionalBlue: rgb2(palette2.blue),
1020
+ rainbow_red: rgb2(palette2.red),
1021
+ rainbow_orange: rgb2(palette2.orange),
1022
+ rainbow_yellow: rgb2(palette2.yellow),
1023
+ rainbow_green: rgb2(palette2.green),
1024
+ rainbow_blue: rgb2(palette2.cyan),
1025
+ rainbow_indigo: rgb2(palette2.blue),
1026
+ rainbow_violet: rgb2(palette2.core),
1027
+ rainbow_red_shimmer: lighten2(palette2.red, 0.35),
1028
+ rainbow_orange_shimmer: lighten2(palette2.orange, 0.35),
1029
+ rainbow_yellow_shimmer: lighten2(palette2.yellow, 0.25),
1030
+ rainbow_green_shimmer: lighten2(palette2.green, 0.35),
1031
+ rainbow_blue_shimmer: lighten2(palette2.cyan, 0.35),
1032
+ rainbow_indigo_shimmer: lighten2(palette2.blue, 0.35),
1033
+ rainbow_violet_shimmer: lighten2(palette2.core, 0.35),
1034
+ clawd_body: rgb2(palette2.core),
1035
+ clawd_background: rgb2(palette2.base),
1036
+ userMessageBackground: rgb2(palette2.panel),
1037
+ bashMessageBackgroundColor: rgb2(palette2.surface),
1038
+ memoryBackgroundColor: tint(palette2.panel, 0.2),
1039
+ rate_limit_fill: rgb2(palette2.core),
1040
+ rate_limit_empty: rgb2(palette2.borderStrong)
1041
+ }
1042
+ };
1043
+ };
1044
+ var pulseTheme = makeTheme();
1045
+ var buildMinimaxTweakccConfig = () => ({
1046
+ ccVersion: "",
1047
+ ccInstallationPath: null,
1048
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
1049
+ changesApplied: false,
1050
+ hidePiebaldAnnouncement: true,
1051
+ settings: {
1052
+ themes: [pulseTheme, ...DEFAULT_THEMES],
1053
+ thinkingVerbs: {
1054
+ format: "{}... ",
1055
+ verbs: [
1056
+ "Resonating",
1057
+ "Prisming",
1058
+ "Spectralizing",
1059
+ "Phase-locking",
1060
+ "Lensing",
1061
+ "Vectorizing",
1062
+ "Harmonizing",
1063
+ "Amplifying",
1064
+ "Stabilizing",
1065
+ "Optimizing",
1066
+ "Synthesizing",
1067
+ "Refining",
1068
+ "Resolving",
1069
+ "Transmitting"
1070
+ ]
1071
+ },
1072
+ thinkingStyle: {
1073
+ updateInterval: 90,
1074
+ phases: ["\xB7", "\u2022", "\u25E6", "\u2022"],
1075
+ reverseMirror: false
1076
+ },
1077
+ userMessageDisplay: {
1078
+ format: formatUserMessage(getUserLabel()),
1079
+ styling: ["bold"],
1080
+ foregroundColor: "default",
1081
+ backgroundColor: "default",
1082
+ borderStyle: "topBottomDouble",
1083
+ borderColor: rgb2(palette2.core),
1084
+ paddingX: 1,
1085
+ paddingY: 0,
1086
+ fitBoxToContent: true
1087
+ },
1088
+ inputBox: {
1089
+ removeBorder: true
1090
+ },
1091
+ misc: {
1092
+ showTweakccVersion: false,
1093
+ showPatchesApplied: false,
1094
+ expandThinkingBlocks: true,
1095
+ enableConversationTitle: true,
1096
+ hideStartupBanner: true,
1097
+ hideCtrlGToEditPrompt: true,
1098
+ hideStartupClawd: true,
1099
+ increaseFileReadLimit: true
1100
+ },
1101
+ toolsets: [
1102
+ {
1103
+ name: "minimax",
1104
+ allowedTools: "*"
1105
+ }
1106
+ ],
1107
+ defaultToolset: "minimax",
1108
+ planModeToolset: "minimax"
1109
+ }
1110
+ });
1111
+
1112
+ // src/brands/openrouter.ts
1113
+ var clamp3 = (value) => Math.max(0, Math.min(255, Math.round(value)));
1114
+ var hexToRgb3 = (hex) => {
1115
+ const normalized = hex.replace("#", "").trim();
1116
+ if (normalized.length === 3) {
1117
+ const [r, g, b] = normalized.split("");
1118
+ return {
1119
+ r: clamp3(parseInt(r + r, 16)),
1120
+ g: clamp3(parseInt(g + g, 16)),
1121
+ b: clamp3(parseInt(b + b, 16))
1122
+ };
1123
+ }
1124
+ if (normalized.length !== 6) {
1125
+ throw new Error(`Unsupported hex color: ${hex}`);
1126
+ }
1127
+ return {
1128
+ r: clamp3(parseInt(normalized.slice(0, 2), 16)),
1129
+ g: clamp3(parseInt(normalized.slice(2, 4), 16)),
1130
+ b: clamp3(parseInt(normalized.slice(4, 6), 16))
1131
+ };
1132
+ };
1133
+ var rgb3 = (hex) => {
1134
+ const { r, g, b } = hexToRgb3(hex);
1135
+ return `rgb(${r},${g},${b})`;
1136
+ };
1137
+ var mix3 = (hexA, hexB, weight) => {
1138
+ const a = hexToRgb3(hexA);
1139
+ const b = hexToRgb3(hexB);
1140
+ const w = Math.max(0, Math.min(1, weight));
1141
+ return `rgb(${clamp3(a.r + (b.r - a.r) * w)},${clamp3(a.g + (b.g - a.g) * w)},${clamp3(a.b + (b.b - a.b) * w)})`;
1142
+ };
1143
+ var lighten3 = (hex, weight) => mix3(hex, "#ffffff", weight);
1144
+ var palette3 = {
1145
+ base: "#eef1f4",
1146
+ surface: "#f5f7f9",
1147
+ panel: "#e5e9ee",
1148
+ border: "#b0d4d4",
1149
+ borderStrong: "#7ec4c4",
1150
+ text: "#1f2430",
1151
+ textMuted: "#475569",
1152
+ textDim: "#6b7280",
1153
+ // Teal/cyan palette matching ASCII art colors
1154
+ teal: "#00a896",
1155
+ // Primary teal (matches ANSI 43)
1156
+ tealBright: "#00d4aa",
1157
+ // Bright teal (matches ANSI 49)
1158
+ tealDeep: "#008080",
1159
+ // Deep teal (matches ANSI 37)
1160
+ tealMuted: "#5fb3a8",
1161
+ // Softer teal for accents
1162
+ cyan: "#20b2aa",
1163
+ green: "#2f9b6d",
1164
+ red: "#d04b5a",
1165
+ orange: "#d28a3c",
1166
+ purple: "#6a5ad6"
1167
+ };
1168
+ var theme2 = {
1169
+ name: "OpenRouter Teal",
1170
+ id: "openrouter-teal",
1171
+ colors: {
1172
+ autoAccept: rgb3(palette3.green),
1173
+ bashBorder: rgb3(palette3.teal),
1174
+ claude: rgb3(palette3.tealDeep),
1175
+ claudeShimmer: lighten3(palette3.teal, 0.35),
1176
+ claudeBlue_FOR_SYSTEM_SPINNER: rgb3(palette3.teal),
1177
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: lighten3(palette3.tealMuted, 0.2),
1178
+ permission: rgb3(palette3.cyan),
1179
+ permissionShimmer: lighten3(palette3.cyan, 0.25),
1180
+ planMode: rgb3(palette3.tealDeep),
1181
+ ide: rgb3(palette3.tealMuted),
1182
+ promptBorder: rgb3(palette3.border),
1183
+ promptBorderShimmer: rgb3(palette3.borderStrong),
1184
+ text: rgb3(palette3.text),
1185
+ inverseText: rgb3(palette3.base),
1186
+ inactive: rgb3(palette3.textDim),
1187
+ subtle: mix3(palette3.base, palette3.tealMuted, 0.12),
1188
+ suggestion: rgb3(palette3.tealMuted),
1189
+ remember: rgb3(palette3.tealDeep),
1190
+ background: rgb3(palette3.base),
1191
+ success: rgb3(palette3.green),
1192
+ error: rgb3(palette3.red),
1193
+ warning: rgb3(palette3.orange),
1194
+ warningShimmer: lighten3(palette3.orange, 0.28),
1195
+ diffAdded: mix3(palette3.base, palette3.green, 0.2),
1196
+ diffRemoved: mix3(palette3.base, palette3.red, 0.2),
1197
+ diffAddedDimmed: mix3(palette3.base, palette3.green, 0.12),
1198
+ diffRemovedDimmed: mix3(palette3.base, palette3.red, 0.12),
1199
+ diffAddedWord: mix3(palette3.base, palette3.green, 0.42),
1200
+ diffRemovedWord: mix3(palette3.base, palette3.red, 0.42),
1201
+ diffAddedWordDimmed: mix3(palette3.base, palette3.green, 0.28),
1202
+ diffRemovedWordDimmed: mix3(palette3.base, palette3.red, 0.28),
1203
+ red_FOR_SUBAGENTS_ONLY: rgb3(palette3.red),
1204
+ blue_FOR_SUBAGENTS_ONLY: rgb3(palette3.tealDeep),
1205
+ green_FOR_SUBAGENTS_ONLY: rgb3(palette3.green),
1206
+ yellow_FOR_SUBAGENTS_ONLY: rgb3(palette3.orange),
1207
+ purple_FOR_SUBAGENTS_ONLY: rgb3(palette3.purple),
1208
+ orange_FOR_SUBAGENTS_ONLY: rgb3(palette3.orange),
1209
+ pink_FOR_SUBAGENTS_ONLY: lighten3(palette3.purple, 0.32),
1210
+ cyan_FOR_SUBAGENTS_ONLY: rgb3(palette3.cyan),
1211
+ professionalBlue: rgb3(palette3.teal),
1212
+ rainbow_red: rgb3(palette3.red),
1213
+ rainbow_orange: rgb3(palette3.orange),
1214
+ rainbow_yellow: lighten3(palette3.orange, 0.18),
1215
+ rainbow_green: rgb3(palette3.green),
1216
+ rainbow_blue: rgb3(palette3.tealMuted),
1217
+ rainbow_indigo: rgb3(palette3.tealDeep),
1218
+ rainbow_violet: rgb3(palette3.purple),
1219
+ rainbow_red_shimmer: lighten3(palette3.red, 0.35),
1220
+ rainbow_orange_shimmer: lighten3(palette3.orange, 0.35),
1221
+ rainbow_yellow_shimmer: lighten3(palette3.orange, 0.3),
1222
+ rainbow_green_shimmer: lighten3(palette3.green, 0.35),
1223
+ rainbow_blue_shimmer: lighten3(palette3.tealMuted, 0.35),
1224
+ rainbow_indigo_shimmer: lighten3(palette3.tealDeep, 0.35),
1225
+ rainbow_violet_shimmer: lighten3(palette3.purple, 0.35),
1226
+ clawd_body: rgb3(palette3.tealDeep),
1227
+ clawd_background: rgb3(palette3.base),
1228
+ userMessageBackground: rgb3(palette3.panel),
1229
+ bashMessageBackgroundColor: rgb3(palette3.surface),
1230
+ memoryBackgroundColor: mix3(palette3.panel, palette3.tealMuted, 0.12),
1231
+ rate_limit_fill: rgb3(palette3.teal),
1232
+ rate_limit_empty: rgb3(palette3.borderStrong)
1233
+ }
1234
+ };
1235
+ var buildOpenRouterTweakccConfig = () => ({
1236
+ ccVersion: "",
1237
+ ccInstallationPath: null,
1238
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
1239
+ changesApplied: false,
1240
+ hidePiebaldAnnouncement: true,
1241
+ settings: {
1242
+ themes: [theme2, ...DEFAULT_THEMES],
1243
+ thinkingVerbs: {
1244
+ format: "{}... ",
1245
+ verbs: [
1246
+ "Routing",
1247
+ "Switchboarding",
1248
+ "Proxying",
1249
+ "Negotiating",
1250
+ "Handshake",
1251
+ "Bridging",
1252
+ "Mapping",
1253
+ "Tunneling",
1254
+ "Resolving",
1255
+ "Balancing",
1256
+ "Rewriting",
1257
+ "Indexing",
1258
+ "Synchronizing",
1259
+ "Aligning"
1260
+ ]
1261
+ },
1262
+ thinkingStyle: {
1263
+ updateInterval: 120,
1264
+ phases: ["\xB7", "\u2022", "\u25E6", "\u2022"],
1265
+ reverseMirror: false
1266
+ },
1267
+ userMessageDisplay: {
1268
+ format: formatUserMessage(getUserLabel()),
1269
+ styling: ["bold"],
1270
+ foregroundColor: "default",
1271
+ backgroundColor: "default",
1272
+ borderStyle: "topBottomSingle",
1273
+ borderColor: rgb3(palette3.teal),
1274
+ paddingX: 1,
1275
+ paddingY: 0,
1276
+ fitBoxToContent: true
1277
+ },
1278
+ inputBox: {
1279
+ removeBorder: true
1280
+ },
1281
+ misc: {
1282
+ showTweakccVersion: false,
1283
+ showPatchesApplied: false,
1284
+ expandThinkingBlocks: true,
1285
+ enableConversationTitle: true,
1286
+ hideStartupBanner: true,
1287
+ hideCtrlGToEditPrompt: true,
1288
+ hideStartupClawd: true,
1289
+ increaseFileReadLimit: true
1290
+ },
1291
+ toolsets: [
1292
+ {
1293
+ name: "openrouter",
1294
+ allowedTools: "*"
1295
+ }
1296
+ ],
1297
+ defaultToolset: "openrouter",
1298
+ planModeToolset: "openrouter"
1299
+ }
1300
+ });
1301
+
1302
+ // src/brands/ccrouter.ts
1303
+ var clamp4 = (value) => Math.max(0, Math.min(255, Math.round(value)));
1304
+ var hexToRgb4 = (hex) => {
1305
+ const normalized = hex.replace("#", "").trim();
1306
+ if (normalized.length === 3) {
1307
+ const [r, g, b] = normalized.split("");
1308
+ return {
1309
+ r: clamp4(parseInt(r + r, 16)),
1310
+ g: clamp4(parseInt(g + g, 16)),
1311
+ b: clamp4(parseInt(b + b, 16))
1312
+ };
1313
+ }
1314
+ if (normalized.length !== 6) {
1315
+ throw new Error(`Unsupported hex color: ${hex}`);
1316
+ }
1317
+ return {
1318
+ r: clamp4(parseInt(normalized.slice(0, 2), 16)),
1319
+ g: clamp4(parseInt(normalized.slice(2, 4), 16)),
1320
+ b: clamp4(parseInt(normalized.slice(4, 6), 16))
1321
+ };
1322
+ };
1323
+ var rgb4 = (hex) => {
1324
+ const { r, g, b } = hexToRgb4(hex);
1325
+ return `rgb(${r},${g},${b})`;
1326
+ };
1327
+ var mix4 = (hexA, hexB, weight) => {
1328
+ const a = hexToRgb4(hexA);
1329
+ const b = hexToRgb4(hexB);
1330
+ const w = Math.max(0, Math.min(1, weight));
1331
+ return `rgb(${clamp4(a.r + (b.r - a.r) * w)},${clamp4(a.g + (b.g - a.g) * w)},${clamp4(a.b + (b.b - a.b) * w)})`;
1332
+ };
1333
+ var lighten4 = (hex, weight) => mix4(hex, "#ffffff", weight);
1334
+ var palette4 = {
1335
+ base: "#edf6ff",
1336
+ surface: "#f6fbff",
1337
+ panel: "#e3f0fb",
1338
+ border: "#c5d9ee",
1339
+ borderStrong: "#9fb9d8",
1340
+ text: "#162434",
1341
+ textMuted: "#3f546b",
1342
+ textDim: "#6b7f95",
1343
+ sky: "#4da3ff",
1344
+ skySoft: "#7bbcff",
1345
+ skyDeep: "#2a6fcb",
1346
+ cyan: "#6cc7ff",
1347
+ green: "#3aa876",
1348
+ red: "#d34e5c",
1349
+ orange: "#e59b3e",
1350
+ purple: "#6b7dd8"
1351
+ };
1352
+ var theme3 = {
1353
+ name: "CCRouter Sky",
1354
+ id: "ccrouter-sky",
1355
+ colors: {
1356
+ autoAccept: rgb4(palette4.green),
1357
+ bashBorder: rgb4(palette4.sky),
1358
+ claude: rgb4(palette4.skyDeep),
1359
+ claudeShimmer: lighten4(palette4.sky, 0.35),
1360
+ claudeBlue_FOR_SYSTEM_SPINNER: rgb4(palette4.sky),
1361
+ claudeBlueShimmer_FOR_SYSTEM_SPINNER: lighten4(palette4.skySoft, 0.2),
1362
+ permission: rgb4(palette4.cyan),
1363
+ permissionShimmer: lighten4(palette4.cyan, 0.25),
1364
+ planMode: rgb4(palette4.skyDeep),
1365
+ ide: rgb4(palette4.cyan),
1366
+ promptBorder: rgb4(palette4.border),
1367
+ promptBorderShimmer: rgb4(palette4.borderStrong),
1368
+ text: rgb4(palette4.text),
1369
+ inverseText: rgb4(palette4.base),
1370
+ inactive: rgb4(palette4.textDim),
1371
+ subtle: mix4(palette4.base, palette4.skySoft, 0.15),
1372
+ suggestion: rgb4(palette4.skySoft),
1373
+ remember: rgb4(palette4.skyDeep),
1374
+ background: rgb4(palette4.base),
1375
+ success: rgb4(palette4.green),
1376
+ error: rgb4(palette4.red),
1377
+ warning: rgb4(palette4.orange),
1378
+ warningShimmer: lighten4(palette4.orange, 0.28),
1379
+ diffAdded: mix4(palette4.base, palette4.green, 0.2),
1380
+ diffRemoved: mix4(palette4.base, palette4.red, 0.2),
1381
+ diffAddedDimmed: mix4(palette4.base, palette4.green, 0.12),
1382
+ diffRemovedDimmed: mix4(palette4.base, palette4.red, 0.12),
1383
+ diffAddedWord: mix4(palette4.base, palette4.green, 0.42),
1384
+ diffRemovedWord: mix4(palette4.base, palette4.red, 0.42),
1385
+ diffAddedWordDimmed: mix4(palette4.base, palette4.green, 0.28),
1386
+ diffRemovedWordDimmed: mix4(palette4.base, palette4.red, 0.28),
1387
+ red_FOR_SUBAGENTS_ONLY: rgb4(palette4.red),
1388
+ blue_FOR_SUBAGENTS_ONLY: rgb4(palette4.skyDeep),
1389
+ green_FOR_SUBAGENTS_ONLY: rgb4(palette4.green),
1390
+ yellow_FOR_SUBAGENTS_ONLY: rgb4(palette4.orange),
1391
+ purple_FOR_SUBAGENTS_ONLY: rgb4(palette4.purple),
1392
+ orange_FOR_SUBAGENTS_ONLY: rgb4(palette4.orange),
1393
+ pink_FOR_SUBAGENTS_ONLY: lighten4(palette4.purple, 0.32),
1394
+ cyan_FOR_SUBAGENTS_ONLY: rgb4(palette4.cyan),
1395
+ professionalBlue: rgb4(palette4.sky),
1396
+ rainbow_red: rgb4(palette4.red),
1397
+ rainbow_orange: rgb4(palette4.orange),
1398
+ rainbow_yellow: lighten4(palette4.orange, 0.18),
1399
+ rainbow_green: rgb4(palette4.green),
1400
+ rainbow_blue: rgb4(palette4.skySoft),
1401
+ rainbow_indigo: rgb4(palette4.skyDeep),
1402
+ rainbow_violet: rgb4(palette4.purple),
1403
+ rainbow_red_shimmer: lighten4(palette4.red, 0.35),
1404
+ rainbow_orange_shimmer: lighten4(palette4.orange, 0.35),
1405
+ rainbow_yellow_shimmer: lighten4(palette4.orange, 0.3),
1406
+ rainbow_green_shimmer: lighten4(palette4.green, 0.35),
1407
+ rainbow_blue_shimmer: lighten4(palette4.skySoft, 0.35),
1408
+ rainbow_indigo_shimmer: lighten4(palette4.skyDeep, 0.35),
1409
+ rainbow_violet_shimmer: lighten4(palette4.purple, 0.35),
1410
+ clawd_body: rgb4(palette4.skyDeep),
1411
+ clawd_background: rgb4(palette4.base),
1412
+ userMessageBackground: rgb4(palette4.panel),
1413
+ bashMessageBackgroundColor: rgb4(palette4.surface),
1414
+ memoryBackgroundColor: mix4(palette4.panel, palette4.skySoft, 0.12),
1415
+ rate_limit_fill: rgb4(palette4.sky),
1416
+ rate_limit_empty: rgb4(palette4.borderStrong)
1417
+ }
1418
+ };
1419
+ var buildCCRouterTweakccConfig = () => ({
1420
+ ccVersion: "",
1421
+ ccInstallationPath: null,
1422
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
1423
+ changesApplied: false,
1424
+ hidePiebaldAnnouncement: true,
1425
+ settings: {
1426
+ themes: [theme3, ...DEFAULT_THEMES],
1427
+ thinkingVerbs: {
1428
+ format: "{}... ",
1429
+ verbs: [
1430
+ "Routing",
1431
+ "Switching",
1432
+ "Proxying",
1433
+ "Forwarding",
1434
+ "Dispatching",
1435
+ "Negotiating",
1436
+ "Bridging",
1437
+ "Mapping",
1438
+ "Tunneling",
1439
+ "Resolving",
1440
+ "Balancing",
1441
+ "Indexing",
1442
+ "Synchronizing",
1443
+ "Finalizing"
1444
+ ]
1445
+ },
1446
+ thinkingStyle: {
1447
+ updateInterval: 115,
1448
+ phases: ["\xB7", "\u2022", "\u25E6", "\u2022"],
1449
+ reverseMirror: false
1450
+ },
1451
+ userMessageDisplay: {
1452
+ format: formatUserMessage(getUserLabel()),
1453
+ styling: ["bold"],
1454
+ foregroundColor: "default",
1455
+ backgroundColor: "default",
1456
+ borderStyle: "topBottomDouble",
1457
+ borderColor: rgb4(palette4.sky),
1458
+ paddingX: 1,
1459
+ paddingY: 0,
1460
+ fitBoxToContent: true
1461
+ },
1462
+ inputBox: {
1463
+ removeBorder: true
1464
+ },
1465
+ misc: {
1466
+ showTweakccVersion: false,
1467
+ showPatchesApplied: false,
1468
+ expandThinkingBlocks: true,
1469
+ enableConversationTitle: true,
1470
+ hideStartupBanner: true,
1471
+ hideCtrlGToEditPrompt: true,
1472
+ hideStartupClawd: true,
1473
+ increaseFileReadLimit: true
1474
+ },
1475
+ toolsets: [
1476
+ {
1477
+ name: "ccrouter",
1478
+ allowedTools: "*"
1479
+ }
1480
+ ],
1481
+ defaultToolset: "ccrouter",
1482
+ planModeToolset: "ccrouter"
1483
+ }
1484
+ });
1485
+
1486
+ // src/brands/index.ts
1487
+ var BRAND_PRESETS = {
1488
+ zai: {
1489
+ key: "zai",
1490
+ label: "Z.ai Carbon",
1491
+ description: "Dark carbon palette, gold + blue accents, Z.ai toolset label.",
1492
+ buildTweakccConfig: buildZaiTweakccConfig
1493
+ },
1494
+ minimax: {
1495
+ key: "minimax",
1496
+ label: "MiniMax Pulse",
1497
+ description: "Vibrant spectrum accents (red/orange/pink/violet) with MiniMax toolset label.",
1498
+ buildTweakccConfig: buildMinimaxTweakccConfig
1499
+ },
1500
+ openrouter: {
1501
+ key: "openrouter",
1502
+ label: "OpenRouter Teal",
1503
+ description: "Light UI with teal/cyan accents and OpenRouter toolset label.",
1504
+ buildTweakccConfig: buildOpenRouterTweakccConfig
1505
+ },
1506
+ ccrouter: {
1507
+ key: "ccrouter",
1508
+ label: "CCRouter Sky",
1509
+ description: "Airy sky-blue accents for Claude Code Router.",
1510
+ buildTweakccConfig: buildCCRouterTweakccConfig
1511
+ }
1512
+ };
1513
+ var listBrandPresets = () => Object.values(BRAND_PRESETS);
1514
+ var resolveBrandKey = (providerKey, requested) => {
1515
+ const normalized = requested?.trim().toLowerCase();
1516
+ if (!normalized || normalized === "auto") {
1517
+ return BRAND_PRESETS[providerKey] ? providerKey : null;
1518
+ }
1519
+ if (normalized === "none" || normalized === "default" || normalized === "off") {
1520
+ return null;
1521
+ }
1522
+ if (!BRAND_PRESETS[normalized]) {
1523
+ throw new Error(`Unknown brand preset: ${requested}`);
1524
+ }
1525
+ return normalized;
1526
+ };
1527
+ var buildBrandConfig = (brandKey) => {
1528
+ const preset = BRAND_PRESETS[brandKey];
1529
+ if (!preset) {
1530
+ throw new Error(`Unknown brand preset: ${brandKey}`);
1531
+ }
1532
+ return preset.buildTweakccConfig();
1533
+ };
1534
+ var getBrandThemeId = (brandKey) => {
1535
+ if (!brandKey) return null;
1536
+ const config = buildBrandConfig(brandKey);
1537
+ const theme4 = config.settings?.themes?.[0];
1538
+ return theme4?.id ?? null;
1539
+ };
1540
+
1541
+ // src/core/tweakcc.ts
1542
+ var require2 = createRequire(import.meta.url);
1543
+ var ensureTweakccConfig = (tweakDir, brandKey) => {
1544
+ if (!brandKey) return false;
1545
+ const configPath = path4.join(tweakDir, "config.json");
1546
+ const brandConfig = buildBrandConfig(brandKey);
1547
+ const desiredDisplay = brandConfig.settings.userMessageDisplay;
1548
+ const normalizeFormat = (format) => (format || "").replace(/\s+/g, "").toLowerCase();
1549
+ const legacyFormats = /* @__PURE__ */ new Set(["[z.ai]{}", "[minimax]{}"]);
1550
+ const themeMatches = (a, b) => !!a?.id && !!b?.id && a.id === b.id || !!a?.name && !!b?.name && a.name === b.name;
1551
+ if (fs3.existsSync(configPath)) {
1552
+ try {
1553
+ const existing = JSON.parse(fs3.readFileSync(configPath, "utf8"));
1554
+ let existingThemes = Array.isArray(existing.settings?.themes) ? existing.settings?.themes : [];
1555
+ const brandThemes = Array.isArray(brandConfig.settings.themes) ? brandConfig.settings.themes : [];
1556
+ const brandThemeId = brandThemes[0]?.id;
1557
+ const looksLikeLegacy = existingThemes.length === 1 && brandThemeId && existingThemes[0]?.id === brandThemeId;
1558
+ let didUpdate = false;
1559
+ if (brandKey === "minimax" && existingThemes.length > 0) {
1560
+ const filtered = existingThemes.filter(
1561
+ (theme4) => theme4?.id !== "minimax-ember" && theme4?.id !== "minimax-glass" && theme4?.id !== "minimax-blade" && theme4?.name !== "MiniMax Ember" && theme4?.name !== "MiniMax Glass" && theme4?.name !== "MiniMax Blade"
1562
+ );
1563
+ if (filtered.length !== existingThemes.length) {
1564
+ existingThemes = filtered;
1565
+ existing.settings = { ...existing.settings, themes: existingThemes };
1566
+ didUpdate = true;
1567
+ }
1568
+ }
1569
+ if (looksLikeLegacy) {
1570
+ existing.settings = { ...brandConfig.settings, ...existing.settings, themes: brandConfig.settings.themes };
1571
+ didUpdate = true;
1572
+ }
1573
+ const existingDisplay = existing.settings?.userMessageDisplay;
1574
+ const desiredMisc = brandConfig.settings.misc;
1575
+ if (desiredDisplay) {
1576
+ if (!existingDisplay) {
1577
+ existing.settings = { ...existing.settings, userMessageDisplay: desiredDisplay };
1578
+ didUpdate = true;
1579
+ } else {
1580
+ const existingFormat = normalizeFormat(existingDisplay.format);
1581
+ const desiredFormat = normalizeFormat(desiredDisplay.format);
1582
+ if (legacyFormats.has(existingFormat) && existingFormat !== desiredFormat) {
1583
+ existing.settings = {
1584
+ ...existing.settings,
1585
+ userMessageDisplay: { ...desiredDisplay, ...existingDisplay, format: desiredDisplay.format }
1586
+ };
1587
+ didUpdate = true;
1588
+ }
1589
+ }
1590
+ }
1591
+ if (desiredMisc) {
1592
+ const existingMisc = existing.settings?.misc || {};
1593
+ const nextMisc = { ...existingMisc, ...desiredMisc };
1594
+ const miscChanged = Object.entries(desiredMisc).some(
1595
+ ([key, value]) => existingMisc[key] !== value
1596
+ );
1597
+ if (miscChanged) {
1598
+ existing.settings = { ...existing.settings, misc: nextMisc };
1599
+ didUpdate = true;
1600
+ }
1601
+ }
1602
+ if (brandThemes.length > 0) {
1603
+ const mergedThemes = [
1604
+ ...brandThemes,
1605
+ ...existingThemes.filter((existingTheme) => !brandThemes.some((theme4) => themeMatches(existingTheme, theme4)))
1606
+ ];
1607
+ const sameLength = mergedThemes.length === existingThemes.length;
1608
+ const sameOrder = sameLength && mergedThemes.every((theme4, idx) => themeMatches(theme4, existingThemes[idx]));
1609
+ if (!sameOrder) {
1610
+ existing.settings = { ...existing.settings, themes: mergedThemes };
1611
+ didUpdate = true;
1612
+ }
1613
+ }
1614
+ if (didUpdate) {
1615
+ fs3.writeFileSync(configPath, JSON.stringify(existing, null, 2));
1616
+ return true;
1617
+ }
1618
+ } catch {
1619
+ }
1620
+ return false;
1621
+ }
1622
+ fs3.writeFileSync(configPath, JSON.stringify(brandConfig, null, 2));
1623
+ return true;
1624
+ };
1625
+ var resolveLocalTweakcc = (args) => {
1626
+ try {
1627
+ const entry = require2.resolve("tweakcc/dist/index.js");
1628
+ return { cmd: process.execPath, args: [entry, ...args] };
1629
+ } catch {
1630
+ return null;
1631
+ }
1632
+ };
1633
+ var runTweakcc = (tweakDir, binaryPath, stdio = "inherit") => {
1634
+ const env = {
1635
+ ...process.env,
1636
+ TWEAKCC_CONFIG_DIR: tweakDir,
1637
+ TWEAKCC_CC_INSTALLATION_PATH: binaryPath
1638
+ };
1639
+ const local = resolveLocalTweakcc(["--apply"]);
1640
+ if (local) {
1641
+ const result2 = spawnSync2(local.cmd, local.args, { stdio: "pipe", env, encoding: "utf8" });
1642
+ if (stdio === "inherit") {
1643
+ if (result2.stdout) process.stdout.write(result2.stdout);
1644
+ if (result2.stderr) process.stderr.write(result2.stderr);
1645
+ }
1646
+ return result2;
1647
+ }
1648
+ if (commandExists("tweakcc")) {
1649
+ const result2 = spawnSync2("tweakcc", ["--apply"], { stdio: "pipe", env, encoding: "utf8" });
1650
+ if (stdio === "inherit") {
1651
+ if (result2.stdout) process.stdout.write(result2.stdout);
1652
+ if (result2.stderr) process.stderr.write(result2.stderr);
1653
+ }
1654
+ return result2;
1655
+ }
1656
+ if (!commandExists("npx")) {
1657
+ return { status: 1, stderr: "npx not found", stdout: "" };
1658
+ }
1659
+ const result = spawnSync2("npx", [`tweakcc@${TWEAKCC_VERSION}`, "--apply"], { stdio: "pipe", env, encoding: "utf8" });
1660
+ if (stdio === "inherit") {
1661
+ if (result.stdout) process.stdout.write(result.stdout);
1662
+ if (result.stderr) process.stderr.write(result.stderr);
1663
+ }
1664
+ return result;
1665
+ };
1666
+ var launchTweakccUi = (tweakDir, binaryPath) => {
1667
+ const env = {
1668
+ ...process.env,
1669
+ TWEAKCC_CONFIG_DIR: tweakDir,
1670
+ TWEAKCC_CC_INSTALLATION_PATH: binaryPath
1671
+ };
1672
+ const local = resolveLocalTweakcc([]);
1673
+ if (local) {
1674
+ return spawnSync2(local.cmd, local.args, { stdio: "inherit", env, encoding: "utf8" });
1675
+ }
1676
+ if (commandExists("tweakcc")) {
1677
+ return spawnSync2("tweakcc", [], { stdio: "inherit", env, encoding: "utf8" });
1678
+ }
1679
+ if (!commandExists("npx")) {
1680
+ return { status: 1, stderr: "npx not found", stdout: "" };
1681
+ }
1682
+ return spawnSync2("npx", [`tweakcc@${TWEAKCC_VERSION}`], { stdio: "inherit", env, encoding: "utf8" });
1683
+ };
1684
+ var spawnTweakccAsync = (cmd, args, env, stdio) => {
1685
+ return new Promise((resolve) => {
1686
+ const child = spawn(cmd, args, { stdio: "pipe", env });
1687
+ let stdout = "";
1688
+ let stderr = "";
1689
+ child.stdout?.on("data", (d) => {
1690
+ stdout += d.toString();
1691
+ if (stdio === "inherit") process.stdout.write(d);
1692
+ });
1693
+ child.stderr?.on("data", (d) => {
1694
+ stderr += d.toString();
1695
+ if (stdio === "inherit") process.stderr.write(d);
1696
+ });
1697
+ child.on("close", (status) => {
1698
+ resolve({ status, stdout, stderr });
1699
+ });
1700
+ child.on("error", (err) => {
1701
+ resolve({ status: 1, stdout: "", stderr: err.message });
1702
+ });
1703
+ });
1704
+ };
1705
+ var runTweakccAsync = async (tweakDir, binaryPath, stdio = "inherit") => {
1706
+ const env = {
1707
+ ...process.env,
1708
+ TWEAKCC_CONFIG_DIR: tweakDir,
1709
+ TWEAKCC_CC_INSTALLATION_PATH: binaryPath
1710
+ };
1711
+ const local = resolveLocalTweakcc(["--apply"]);
1712
+ if (local) {
1713
+ return spawnTweakccAsync(local.cmd, local.args, env, stdio);
1714
+ }
1715
+ if (commandExists("tweakcc")) {
1716
+ return spawnTweakccAsync("tweakcc", ["--apply"], env, stdio);
1717
+ }
1718
+ if (!commandExists("npx")) {
1719
+ return { status: 1, stderr: "npx not found", stdout: "" };
1720
+ }
1721
+ return spawnTweakccAsync("npx", [`tweakcc@${TWEAKCC_VERSION}`, "--apply"], env, stdio);
1722
+ };
1723
+
1724
+ // src/core/errors.ts
1725
+ var extractErrorHint = (text) => {
1726
+ const normalized = text.toLowerCase();
1727
+ if (normalized.includes("could not extract js from native binary")) {
1728
+ return "tweakcc reported a native Claude Code binary. cc-mirror uses npm installs only; update or recreate the variant, or run with --no-tweak.";
1729
+ }
1730
+ if (normalized.includes("node-lief")) {
1731
+ return "tweakcc requires node-lief for native Claude Code binaries. cc-mirror uses npm installs only; update or recreate the variant, or run with --no-tweak.";
1732
+ }
1733
+ return null;
1734
+ };
1735
+ var formatTweakccFailure = (output) => {
1736
+ const hint = extractErrorHint(output);
1737
+ if (hint) return hint;
1738
+ const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1739
+ if (lines.length === 0) return "tweakcc failed.";
1740
+ const errorLine = lines.find((line) => line.toLowerCase().startsWith("error:"));
1741
+ if (errorLine) return errorLine;
1742
+ const tail = lines.slice(-3).join(" | ");
1743
+ return tail.length > 0 ? tail : "tweakcc failed.";
1744
+ };
1745
+
1746
+ // src/core/variants.ts
1747
+ import fs4 from "node:fs";
1748
+ import path5 from "node:path";
1749
+ var loadVariantMeta = (variantDir) => {
1750
+ const metaPath = path5.join(variantDir, "variant.json");
1751
+ if (!fs4.existsSync(metaPath)) return null;
1752
+ return readJson(metaPath);
1753
+ };
1754
+ var listVariants = (rootDir) => {
1755
+ if (!fs4.existsSync(rootDir)) return [];
1756
+ return fs4.readdirSync(rootDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => fs4.existsSync(path5.join(rootDir, name, "variant.json"))).map((name) => ({ name, meta: loadVariantMeta(path5.join(rootDir, name)) }));
1757
+ };
1758
+
1759
+ // src/core/variant-builder/VariantBuilder.ts
1760
+ import path15 from "node:path";
1761
+
1762
+ // src/providers/index.ts
1763
+ var DEFAULT_TIMEOUT_MS = "3000000";
1764
+ var CCROUTER_AUTH_FALLBACK = "ccrouter-proxy";
1765
+ var PROVIDERS = {
1766
+ zai: {
1767
+ key: "zai",
1768
+ label: "Zai Cloud",
1769
+ description: "GLM Coding Plan via Anthropic-compatible endpoint",
1770
+ baseUrl: "https://api.z.ai/api/anthropic",
1771
+ env: {
1772
+ API_TIMEOUT_MS: DEFAULT_TIMEOUT_MS,
1773
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
1774
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-4.7",
1775
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-4.7",
1776
+ CC_MIRROR_SPLASH: 1,
1777
+ CC_MIRROR_PROVIDER_LABEL: "Zai Cloud",
1778
+ CC_MIRROR_SPLASH_STYLE: "zai"
1779
+ },
1780
+ apiKeyLabel: "Zai API key"
1781
+ },
1782
+ minimax: {
1783
+ key: "minimax",
1784
+ label: "MiniMax Cloud",
1785
+ description: "MiniMax-M2.1 via Anthropic-compatible endpoint",
1786
+ baseUrl: "https://api.minimax.io/anthropic",
1787
+ env: {
1788
+ API_TIMEOUT_MS: DEFAULT_TIMEOUT_MS,
1789
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1,
1790
+ ANTHROPIC_MODEL: "MiniMax-M2.1",
1791
+ ANTHROPIC_SMALL_FAST_MODEL: "MiniMax-M2.1",
1792
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "MiniMax-M2.1",
1793
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "MiniMax-M2.1",
1794
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "MiniMax-M2.1",
1795
+ CC_MIRROR_SPLASH: 1,
1796
+ CC_MIRROR_PROVIDER_LABEL: "MiniMax Cloud",
1797
+ CC_MIRROR_SPLASH_STYLE: "minimax"
1798
+ },
1799
+ apiKeyLabel: "MiniMax API key"
1800
+ },
1801
+ openrouter: {
1802
+ key: "openrouter",
1803
+ label: "OpenRouter",
1804
+ description: "OpenRouter gateway for Anthropic-compatible requests",
1805
+ baseUrl: "https://openrouter.ai/api",
1806
+ env: {
1807
+ API_TIMEOUT_MS: DEFAULT_TIMEOUT_MS,
1808
+ CC_MIRROR_SPLASH: 1,
1809
+ CC_MIRROR_PROVIDER_LABEL: "OpenRouter",
1810
+ CC_MIRROR_SPLASH_STYLE: "openrouter"
1811
+ },
1812
+ apiKeyLabel: "OpenRouter API key",
1813
+ authMode: "authToken",
1814
+ requiresModelMapping: true
1815
+ },
1816
+ ccrouter: {
1817
+ key: "ccrouter",
1818
+ label: "Claude Code Router",
1819
+ description: "Route requests to any model via Claude Code Router",
1820
+ baseUrl: "http://127.0.0.1:3456",
1821
+ env: {
1822
+ API_TIMEOUT_MS: DEFAULT_TIMEOUT_MS,
1823
+ CC_MIRROR_SPLASH: 1,
1824
+ CC_MIRROR_PROVIDER_LABEL: "Claude Code Router",
1825
+ CC_MIRROR_SPLASH_STYLE: "ccrouter"
1826
+ },
1827
+ apiKeyLabel: "Router URL",
1828
+ authMode: "authToken",
1829
+ requiresModelMapping: false,
1830
+ // Models configured in ~/.claude-code-router/config.json
1831
+ credentialOptional: true
1832
+ // No API key needed - CCRouter handles auth
1833
+ },
1834
+ custom: {
1835
+ key: "custom",
1836
+ label: "Custom",
1837
+ description: "Coming Soon \u2014 Bring your own endpoint",
1838
+ baseUrl: "",
1839
+ env: {
1840
+ API_TIMEOUT_MS: DEFAULT_TIMEOUT_MS
1841
+ },
1842
+ apiKeyLabel: "API key",
1843
+ experimental: true
1844
+ }
1845
+ };
1846
+ var getProvider = (key) => PROVIDERS[key];
1847
+ var listProviders = (includeExperimental = false) => {
1848
+ const providers = Object.values(PROVIDERS);
1849
+ if (includeExperimental) {
1850
+ return providers;
1851
+ }
1852
+ return providers.filter((p) => !p.experimental);
1853
+ };
1854
+ var normalizeModelValue = (value) => (value ?? "").trim();
1855
+ var applyModelOverrides = (env, overrides) => {
1856
+ if (!overrides) return;
1857
+ const entries = [
1858
+ ["ANTHROPIC_DEFAULT_SONNET_MODEL", overrides.sonnet],
1859
+ ["ANTHROPIC_DEFAULT_OPUS_MODEL", overrides.opus],
1860
+ ["ANTHROPIC_DEFAULT_HAIKU_MODEL", overrides.haiku],
1861
+ ["ANTHROPIC_SMALL_FAST_MODEL", overrides.smallFast],
1862
+ ["ANTHROPIC_MODEL", overrides.defaultModel],
1863
+ ["CLAUDE_CODE_SUBAGENT_MODEL", overrides.subagentModel]
1864
+ ];
1865
+ for (const [key, value] of entries) {
1866
+ const trimmed = normalizeModelValue(value);
1867
+ if (trimmed) {
1868
+ env[key] = trimmed;
1869
+ }
1870
+ }
1871
+ };
1872
+ var buildEnv = ({ providerKey, baseUrl, apiKey, extraEnv, modelOverrides }) => {
1873
+ const provider = getProvider(providerKey);
1874
+ if (!provider) {
1875
+ throw new Error(`Unknown provider: ${providerKey}`);
1876
+ }
1877
+ const env = { ...provider.env };
1878
+ const authMode = provider.authMode ?? "apiKey";
1879
+ if (!Object.hasOwn(env, "DISABLE_AUTOUPDATER")) {
1880
+ env.DISABLE_AUTOUPDATER = "1";
1881
+ }
1882
+ if (!Object.hasOwn(env, "CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION")) {
1883
+ env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION = "1";
1884
+ }
1885
+ if (baseUrl) env.ANTHROPIC_BASE_URL = baseUrl;
1886
+ if (authMode === "authToken") {
1887
+ const trimmed = normalizeModelValue(apiKey);
1888
+ if (trimmed) {
1889
+ env.ANTHROPIC_AUTH_TOKEN = trimmed;
1890
+ } else if (providerKey === "ccrouter") {
1891
+ env.ANTHROPIC_AUTH_TOKEN = CCROUTER_AUTH_FALLBACK;
1892
+ }
1893
+ if (Object.hasOwn(env, "ANTHROPIC_API_KEY")) {
1894
+ delete env.ANTHROPIC_API_KEY;
1895
+ }
1896
+ } else if (apiKey) {
1897
+ env.ANTHROPIC_API_KEY = apiKey;
1898
+ env.CC_MIRROR_UNSET_AUTH_TOKEN = "1";
1899
+ if (providerKey === "zai") {
1900
+ env.Z_AI_API_KEY = apiKey;
1901
+ }
1902
+ } else if (authMode === "apiKey") {
1903
+ env.CC_MIRROR_UNSET_AUTH_TOKEN = "1";
1904
+ }
1905
+ applyModelOverrides(env, modelOverrides);
1906
+ if (Array.isArray(extraEnv)) {
1907
+ for (const entry of extraEnv) {
1908
+ const idx = entry.indexOf("=");
1909
+ if (idx === -1) continue;
1910
+ const key = entry.slice(0, idx).trim();
1911
+ const value = entry.slice(idx + 1).trim();
1912
+ if (!key) continue;
1913
+ env[key] = value;
1914
+ }
1915
+ }
1916
+ if (authMode === "authToken" && Object.hasOwn(env, "ANTHROPIC_API_KEY")) {
1917
+ delete env.ANTHROPIC_API_KEY;
1918
+ }
1919
+ if (authMode !== "authToken" && Object.hasOwn(env, "ANTHROPIC_AUTH_TOKEN")) {
1920
+ delete env.ANTHROPIC_AUTH_TOKEN;
1921
+ }
1922
+ return env;
1923
+ };
1924
+
1925
+ // src/core/variant-builder/steps/PrepareDirectoriesStep.ts
1926
+ var PrepareDirectoriesStep = class {
1927
+ name = "PrepareDirectories";
1928
+ execute(ctx) {
1929
+ ctx.report("Preparing directories...");
1930
+ ensureDir(ctx.paths.variantDir);
1931
+ ensureDir(ctx.paths.configDir);
1932
+ ensureDir(ctx.paths.tweakDir);
1933
+ ensureDir(ctx.paths.resolvedBin);
1934
+ ensureDir(ctx.paths.npmDir);
1935
+ }
1936
+ async executeAsync(ctx) {
1937
+ await ctx.report("Preparing directories...");
1938
+ ensureDir(ctx.paths.variantDir);
1939
+ ensureDir(ctx.paths.configDir);
1940
+ ensureDir(ctx.paths.tweakDir);
1941
+ ensureDir(ctx.paths.resolvedBin);
1942
+ ensureDir(ctx.paths.npmDir);
1943
+ }
1944
+ };
1945
+
1946
+ // src/core/install.ts
1947
+ import fs5 from "node:fs";
1948
+ import path6 from "node:path";
1949
+ import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
1950
+ var resolveNpmCliPath = (npmDir, npmPackage) => {
1951
+ const packageParts = npmPackage.split("/");
1952
+ return path6.join(npmDir, "node_modules", ...packageParts, "cli.js");
1953
+ };
1954
+ var installNpmClaude = (params) => {
1955
+ if (!commandExists("npm")) {
1956
+ throw new Error("npm is required for npm-based installs.");
1957
+ }
1958
+ const stdio = params.stdio ?? "inherit";
1959
+ const pkgSpec = params.npmVersion ? `${params.npmPackage}@${params.npmVersion}` : params.npmPackage;
1960
+ const result = spawnSync3("npm", ["install", "--prefix", params.npmDir, "--no-save", pkgSpec], {
1961
+ stdio: "pipe",
1962
+ encoding: "utf8"
1963
+ });
1964
+ if (stdio === "inherit") {
1965
+ if (result.stdout) process.stdout.write(result.stdout);
1966
+ if (result.stderr) process.stderr.write(result.stderr);
1967
+ }
1968
+ if (result.status !== 0) {
1969
+ const output = `${result.stderr ?? ""}
1970
+ ${result.stdout ?? ""}`.trim();
1971
+ const tail = output.length > 0 ? `
1972
+ ${output}` : "";
1973
+ throw new Error(`npm install failed for ${pkgSpec}.${tail}`);
1974
+ }
1975
+ const cliPath = resolveNpmCliPath(params.npmDir, params.npmPackage);
1976
+ if (!fs5.existsSync(cliPath)) {
1977
+ throw new Error(`npm install succeeded but cli.js was not found at ${cliPath}`);
1978
+ }
1979
+ return { cliPath };
1980
+ };
1981
+ var installNpmClaudeAsync = (params) => {
1982
+ return new Promise((resolve, reject) => {
1983
+ if (!commandExists("npm")) {
1984
+ reject(new Error("npm is required for npm-based installs."));
1985
+ return;
1986
+ }
1987
+ const stdio = params.stdio ?? "inherit";
1988
+ const pkgSpec = params.npmVersion ? `${params.npmPackage}@${params.npmVersion}` : params.npmPackage;
1989
+ const child = spawn2("npm", ["install", "--prefix", params.npmDir, "--no-save", pkgSpec], {
1990
+ stdio: "pipe"
1991
+ });
1992
+ let stdout = "";
1993
+ let stderr = "";
1994
+ child.stdout?.on("data", (data) => {
1995
+ stdout += data.toString();
1996
+ if (stdio === "inherit") process.stdout.write(data);
1997
+ });
1998
+ child.stderr?.on("data", (data) => {
1999
+ stderr += data.toString();
2000
+ if (stdio === "inherit") process.stderr.write(data);
2001
+ });
2002
+ child.on("close", (code) => {
2003
+ if (code !== 0) {
2004
+ const output = `${stderr}
2005
+ ${stdout}`.trim();
2006
+ const tail = output.length > 0 ? `
2007
+ ${output}` : "";
2008
+ reject(new Error(`npm install failed for ${pkgSpec}.${tail}`));
2009
+ return;
2010
+ }
2011
+ const cliPath = resolveNpmCliPath(params.npmDir, params.npmPackage);
2012
+ if (!fs5.existsSync(cliPath)) {
2013
+ reject(new Error(`npm install succeeded but cli.js was not found at ${cliPath}`));
2014
+ return;
2015
+ }
2016
+ resolve({ cliPath });
2017
+ });
2018
+ child.on("error", (err) => {
2019
+ reject(new Error(`Failed to spawn npm: ${err.message}`));
2020
+ });
2021
+ });
2022
+ };
2023
+
2024
+ // src/core/variant-builder/steps/InstallNpmStep.ts
2025
+ var InstallNpmStep = class {
2026
+ name = "InstallNpm";
2027
+ execute(ctx) {
2028
+ const { prefs, paths, state } = ctx;
2029
+ ctx.report(`Installing ${prefs.resolvedNpmPackage}@${prefs.resolvedNpmVersion}...`);
2030
+ const install = installNpmClaude({
2031
+ npmDir: paths.npmDir,
2032
+ npmPackage: prefs.resolvedNpmPackage,
2033
+ npmVersion: prefs.resolvedNpmVersion,
2034
+ stdio: prefs.commandStdio
2035
+ });
2036
+ state.binaryPath = install.cliPath;
2037
+ state.claudeBinary = `npm:${prefs.resolvedNpmPackage}@${prefs.resolvedNpmVersion}`;
2038
+ }
2039
+ async executeAsync(ctx) {
2040
+ const { prefs, paths, state } = ctx;
2041
+ await ctx.report(`Installing ${prefs.resolvedNpmPackage}@${prefs.resolvedNpmVersion}...`);
2042
+ const install = await installNpmClaudeAsync({
2043
+ npmDir: paths.npmDir,
2044
+ npmPackage: prefs.resolvedNpmPackage,
2045
+ npmVersion: prefs.resolvedNpmVersion,
2046
+ stdio: prefs.commandStdio
2047
+ });
2048
+ state.binaryPath = install.cliPath;
2049
+ state.claudeBinary = `npm:${prefs.resolvedNpmPackage}@${prefs.resolvedNpmVersion}`;
2050
+ }
2051
+ };
2052
+
2053
+ // src/core/variant-builder/steps/WriteConfigStep.ts
2054
+ import path8 from "node:path";
2055
+
2056
+ // src/core/claude-config.ts
2057
+ import fs6 from "node:fs";
2058
+ import path7 from "node:path";
2059
+ var SETTINGS_FILE = "settings.json";
2060
+ var CLAUDE_CONFIG_FILE = ".claude.json";
2061
+ var PLACEHOLDER_KEY = "<API_KEY>";
2062
+ var toStringOrNull = (value) => {
2063
+ if (typeof value !== "string") return null;
2064
+ const trimmed = value.trim();
2065
+ if (!trimmed || trimmed === PLACEHOLDER_KEY) return null;
2066
+ return trimmed;
2067
+ };
2068
+ var readSettingsApiKey = (configDir) => {
2069
+ const settingsPath = path7.join(configDir, SETTINGS_FILE);
2070
+ const settings = readJson(settingsPath);
2071
+ if (!settings?.env) return null;
2072
+ const env = settings.env;
2073
+ return toStringOrNull(env.ANTHROPIC_API_KEY);
2074
+ };
2075
+ var ZAI_DENY_TOOLS = [
2076
+ "mcp__4_5v_mcp__analyze_image",
2077
+ "mcp__milk_tea_server__claim_milk_tea_coupon",
2078
+ "mcp__web_reader__webReader"
2079
+ ];
2080
+ var ensureZaiMcpDeny = (configDir) => {
2081
+ const settingsPath = path7.join(configDir, SETTINGS_FILE);
2082
+ const existing = readJson(settingsPath) || {};
2083
+ const permissions = existing.permissions || {};
2084
+ const deny = Array.isArray(permissions.deny) ? [...permissions.deny] : [];
2085
+ let changed = false;
2086
+ for (const tool of ZAI_DENY_TOOLS) {
2087
+ if (!deny.includes(tool)) {
2088
+ deny.push(tool);
2089
+ changed = true;
2090
+ }
2091
+ }
2092
+ if (!changed) return false;
2093
+ const next = {
2094
+ ...existing,
2095
+ permissions: {
2096
+ ...permissions,
2097
+ deny
2098
+ }
2099
+ };
2100
+ writeJson(settingsPath, next);
2101
+ return true;
2102
+ };
2103
+ var ensureSettingsEnvDefaults = (configDir, defaults) => {
2104
+ const settingsPath = path7.join(configDir, SETTINGS_FILE);
2105
+ const existing = readJson(settingsPath) || {};
2106
+ const env = { ...existing.env ?? {} };
2107
+ let changed = false;
2108
+ for (const [key, value] of Object.entries(defaults)) {
2109
+ if (!Object.hasOwn(env, key)) {
2110
+ env[key] = value;
2111
+ changed = true;
2112
+ }
2113
+ }
2114
+ if (!changed) return false;
2115
+ writeJson(settingsPath, { ...existing, env });
2116
+ return true;
2117
+ };
2118
+ var ensureSettingsEnvOverrides = (configDir, overrides) => {
2119
+ const settingsPath = path7.join(configDir, SETTINGS_FILE);
2120
+ const existing = readJson(settingsPath) || {};
2121
+ const env = { ...existing.env ?? {} };
2122
+ let changed = false;
2123
+ for (const [key, value] of Object.entries(overrides)) {
2124
+ if (value === void 0) continue;
2125
+ if (env[key] !== value) {
2126
+ env[key] = value;
2127
+ changed = true;
2128
+ }
2129
+ }
2130
+ if (!changed) return false;
2131
+ writeJson(settingsPath, { ...existing, env });
2132
+ return true;
2133
+ };
2134
+ var ensureApiKeyApproval = (configDir, apiKey) => {
2135
+ const resolvedKey = toStringOrNull(apiKey) || readSettingsApiKey(configDir);
2136
+ if (!resolvedKey) return false;
2137
+ const approvedToken = resolvedKey.slice(-20);
2138
+ const configPath = path7.join(configDir, CLAUDE_CONFIG_FILE);
2139
+ const exists = fs6.existsSync(configPath);
2140
+ let config = null;
2141
+ if (exists) {
2142
+ config = readJson(configPath);
2143
+ if (!config) return false;
2144
+ } else {
2145
+ config = {};
2146
+ }
2147
+ const approved = Array.isArray(config.customApiKeyResponses?.approved) ? [...config.customApiKeyResponses.approved] : [];
2148
+ const rejected = Array.isArray(config.customApiKeyResponses?.rejected) ? [...config.customApiKeyResponses.rejected] : [];
2149
+ if (approved.includes(approvedToken)) return false;
2150
+ approved.push(approvedToken);
2151
+ const next = {
2152
+ ...config,
2153
+ customApiKeyResponses: {
2154
+ ...config.customApiKeyResponses,
2155
+ approved,
2156
+ rejected
2157
+ }
2158
+ };
2159
+ writeJson(configPath, next);
2160
+ return true;
2161
+ };
2162
+ var ensureOnboardingState = (configDir, opts = {}) => {
2163
+ const configPath = path7.join(configDir, CLAUDE_CONFIG_FILE);
2164
+ const exists = fs6.existsSync(configPath);
2165
+ let config = null;
2166
+ if (exists) {
2167
+ config = readJson(configPath);
2168
+ if (!config) {
2169
+ return { updated: false, themeChanged: false, onboardingChanged: false };
2170
+ }
2171
+ } else {
2172
+ config = {};
2173
+ }
2174
+ let changed = false;
2175
+ let themeChanged = false;
2176
+ let onboardingChanged = false;
2177
+ if (opts.themeId) {
2178
+ const shouldSetTheme = opts.forceTheme || !config.theme;
2179
+ if (shouldSetTheme && config.theme !== opts.themeId) {
2180
+ config.theme = opts.themeId;
2181
+ changed = true;
2182
+ themeChanged = true;
2183
+ }
2184
+ }
2185
+ if (config.hasCompletedOnboarding !== true) {
2186
+ config.hasCompletedOnboarding = true;
2187
+ changed = true;
2188
+ onboardingChanged = true;
2189
+ }
2190
+ if (!changed) {
2191
+ return { updated: false, themeChanged: false, onboardingChanged: false };
2192
+ }
2193
+ writeJson(configPath, config);
2194
+ return { updated: true, themeChanged, onboardingChanged };
2195
+ };
2196
+ var ensureMinimaxMcpServer = (configDir, apiKey) => {
2197
+ const resolvedKey = toStringOrNull(apiKey) || readSettingsApiKey(configDir);
2198
+ const configPath = path7.join(configDir, CLAUDE_CONFIG_FILE);
2199
+ const exists = fs6.existsSync(configPath);
2200
+ let config = null;
2201
+ if (exists) {
2202
+ config = readJson(configPath);
2203
+ if (!config) return false;
2204
+ } else {
2205
+ config = {};
2206
+ }
2207
+ const existingServers = config.mcpServers ?? {};
2208
+ if (existingServers.MiniMax) return false;
2209
+ const mcpServer = {
2210
+ command: "uvx",
2211
+ args: ["minimax-coding-plan-mcp", "-y"],
2212
+ env: {
2213
+ MINIMAX_API_KEY: resolvedKey ?? "Enter your API key",
2214
+ MINIMAX_API_HOST: "https://api.minimax.io"
2215
+ }
2216
+ };
2217
+ const next = {
2218
+ ...config,
2219
+ mcpServers: {
2220
+ ...existingServers,
2221
+ MiniMax: mcpServer
2222
+ }
2223
+ };
2224
+ writeJson(configPath, next);
2225
+ return true;
2226
+ };
2227
+
2228
+ // src/core/variant-builder/steps/WriteConfigStep.ts
2229
+ var WriteConfigStep = class {
2230
+ name = "WriteConfig";
2231
+ execute(ctx) {
2232
+ this.writeConfig(ctx);
2233
+ }
2234
+ async executeAsync(ctx) {
2235
+ await ctx.report("Writing configuration...");
2236
+ this.writeConfig(ctx);
2237
+ }
2238
+ writeConfig(ctx) {
2239
+ const { params, provider, paths, state } = ctx;
2240
+ ctx.report("Writing configuration...");
2241
+ const env = buildEnv({
2242
+ providerKey: params.providerKey,
2243
+ baseUrl: params.baseUrl,
2244
+ apiKey: params.apiKey,
2245
+ extraEnv: params.extraEnv,
2246
+ modelOverrides: params.modelOverrides
2247
+ });
2248
+ if (!Object.hasOwn(env, "TWEAKCC_CONFIG_DIR")) {
2249
+ env.TWEAKCC_CONFIG_DIR = paths.tweakDir;
2250
+ }
2251
+ const authMode = provider.authMode ?? "apiKey";
2252
+ if (authMode === "apiKey" && !env.ANTHROPIC_API_KEY) {
2253
+ env.ANTHROPIC_API_KEY = "<API_KEY>";
2254
+ }
2255
+ const config = { env };
2256
+ writeJson(path8.join(paths.configDir, "settings.json"), config);
2257
+ state.env = env;
2258
+ state.resolvedApiKey = typeof env.ANTHROPIC_API_KEY === "string" ? env.ANTHROPIC_API_KEY : void 0;
2259
+ ensureApiKeyApproval(paths.configDir, state.resolvedApiKey);
2260
+ if (provider.authMode === "authToken" && !env.ANTHROPIC_AUTH_TOKEN) {
2261
+ state.notes.push("ANTHROPIC_AUTH_TOKEN not set; provider auth may fail.");
2262
+ }
2263
+ if (params.providerKey === "openrouter") {
2264
+ const missing = [];
2265
+ if (!env.ANTHROPIC_DEFAULT_SONNET_MODEL) missing.push("ANTHROPIC_DEFAULT_SONNET_MODEL");
2266
+ if (!env.ANTHROPIC_DEFAULT_OPUS_MODEL) missing.push("ANTHROPIC_DEFAULT_OPUS_MODEL");
2267
+ if (!env.ANTHROPIC_DEFAULT_HAIKU_MODEL) missing.push("ANTHROPIC_DEFAULT_HAIKU_MODEL");
2268
+ if (missing.length > 0) {
2269
+ state.notes.push(`Model mapping incomplete; add ${missing.join(", ")} if needed.`);
2270
+ }
2271
+ state.notes.push("Feature support varies by provider. WebSearch/Image tools may require special models.");
2272
+ }
2273
+ }
2274
+ };
2275
+
2276
+ // src/core/variant-builder/steps/BrandThemeStep.ts
2277
+ var BrandThemeStep = class {
2278
+ name = "BrandTheme";
2279
+ execute(ctx) {
2280
+ ctx.report("Setting up brand theme...");
2281
+ this.setupBrand(ctx);
2282
+ }
2283
+ async executeAsync(ctx) {
2284
+ await ctx.report("Setting up brand theme...");
2285
+ this.setupBrand(ctx);
2286
+ if (ctx.params.providerKey === "minimax") {
2287
+ await ctx.report("Configuring MiniMax MCP server...");
2288
+ }
2289
+ }
2290
+ setupBrand(ctx) {
2291
+ const { params, paths, prefs, state } = ctx;
2292
+ const brandKey = resolveBrandKey(params.providerKey, params.brand);
2293
+ prefs.brandKey = brandKey;
2294
+ ensureTweakccConfig(paths.tweakDir, brandKey);
2295
+ const brandThemeId = !params.noTweak && brandKey ? getBrandThemeId(brandKey) : null;
2296
+ const onboarding = ensureOnboardingState(paths.configDir, {
2297
+ themeId: brandThemeId ?? "dark",
2298
+ forceTheme: Boolean(brandThemeId)
2299
+ });
2300
+ if (onboarding.themeChanged) {
2301
+ state.notes.push(`Default theme set to ${brandThemeId ?? "dark"}.`);
2302
+ }
2303
+ if (onboarding.onboardingChanged) {
2304
+ state.notes.push("Onboarding marked complete.");
2305
+ }
2306
+ if (params.providerKey === "minimax") {
2307
+ ctx.report("Configuring MiniMax MCP server...");
2308
+ ensureMinimaxMcpServer(paths.configDir, state.resolvedApiKey);
2309
+ }
2310
+ if (params.providerKey === "zai") {
2311
+ const blockedZaiTools = ensureZaiMcpDeny(paths.configDir);
2312
+ if (blockedZaiTools) {
2313
+ state.notes.push("Blocked Z.ai-injected MCP tools in settings.json.");
2314
+ }
2315
+ }
2316
+ if (params.noTweak && prefs.promptPackPreference) {
2317
+ state.notes.push(`Prompt pack skipped (tweakcc disabled, ${prefs.promptPackModePreference}).`);
2318
+ }
2319
+ }
2320
+ };
2321
+
2322
+ // src/core/prompt-pack.ts
2323
+ import fs7 from "node:fs";
2324
+ import path9 from "node:path";
2325
+
2326
+ // src/core/prompt-pack/sanitize.ts
2327
+ var BACKTICK_REGEX = /`/g;
2328
+ var sanitizeOverlayText = (text) => text.replace(BACKTICK_REGEX, "'");
2329
+ var sanitizeOverlayMap = (overlays) => {
2330
+ const sanitized = {};
2331
+ for (const [key, value] of Object.entries(overlays)) {
2332
+ if (!value || !value.trim()) continue;
2333
+ sanitized[key] = sanitizeOverlayText(value);
2334
+ }
2335
+ return sanitized;
2336
+ };
2337
+
2338
+ // src/core/prompt-pack/shared.ts
2339
+ var verbositySpec = `
2340
+ <output_verbosity_spec>
2341
+ - Default: 3-6 sentences or <=6 bullets.
2342
+ - For multi-step / multi-file work: 1 short overview paragraph, then <=6 bullets:
2343
+ What changed, Where, How to verify, Risks, Next steps, Open questions.
2344
+ </output_verbosity_spec>
2345
+ `.trim();
2346
+ var operatingSpec = (mode) => `
2347
+ <system_reminder>
2348
+ - Operate like an ambitious, senior engineer: proactive, high-ownership, and precise.
2349
+ - Prefer concrete outputs: commands, file paths, diffs, and validation steps.
2350
+ - Respect permissions and confirm before destructive actions.
2351
+ ${mode === "maximal" ? "- Parallelize independent work with Task/subagents when useful." : ""}
2352
+ </system_reminder>
2353
+ `.trim();
2354
+ var subjectiveWorkSpec = `
2355
+ <subjective_work_guardrails>
2356
+ - For creative, subjective, or open-ended tasks, ask clarifying questions first (use AskUserQuestion when available).
2357
+ - Treat phrases like "impress me", "make it cool", "build something amazing" as signals to clarify preferences, not invitations to execute.
2358
+ - For design or aesthetic work, ask about purpose, audience, style preferences, inspirations, constraints, and tech stack before generating.
2359
+ - When you catch yourself making assumptions about subjective quality, pause and ask instead.
2360
+ </subjective_work_guardrails>
2361
+ `.trim();
2362
+
2363
+ // src/core/prompt-pack/providers/zai.ts
2364
+ var ZAI_BLOCKED_MCP_TOOLS = [
2365
+ "mcp__4_5v_mcp__analyze_image",
2366
+ "mcp__milk_tea_server__claim_milk_tea_coupon",
2367
+ "mcp__web_reader__webReader"
2368
+ ];
2369
+ var ZAI_CLI_TEXT = `
2370
+ # ZAI CLI (embedded reference)
2371
+ Access Z.AI capabilities via 'npx zai-cli'. The CLI is self-documenting - use '--help' at any level.
2372
+
2373
+ Setup:
2374
+ - Required env: 'Z_AI_API_KEY'
2375
+ - Get a key at: https://z.ai/manage-apikey/apikey-list
2376
+
2377
+ Commands:
2378
+ - vision: Analyze images, screenshots, videos (many subcommands)
2379
+ - search: Real-time web search (domain/recency/location/count filters)
2380
+ - read: Fetch web pages as markdown/text (format/no-images/with-links/timeout)
2381
+ - repo: GitHub exploration (tree/search/read). Run npx zai-cli repo --help for subcommands.
2382
+ - tools/tool/call: MCP tool discovery + raw calls (advanced)
2383
+ - code: TypeScript tool chaining (advanced)
2384
+ - doctor: Environment + connectivity checks
2385
+
2386
+ Quick start examples:
2387
+ - npx zai-cli vision analyze ./screenshot.png "What errors do you see?"
2388
+ - npx zai-cli search "React 19 new features" --count 5
2389
+ - npx zai-cli read https://docs.example.com/api
2390
+ - npx zai-cli repo search facebook/react "server components"
2391
+ - npx zai-cli repo --help
2392
+ - npx zai-cli doctor
2393
+
2394
+ Output:
2395
+ - Default: data-only (token efficient).
2396
+ - Use '--output-format json' for { success, data, timestamp } wrapping.
2397
+ `.trim();
2398
+ var buildZaiContract = (mode) => `
2399
+ <explicit_guidance>
2400
+ Provider: z.ai (GLM)
2401
+
2402
+ <authentication>
2403
+ - Use API-key auth only.
2404
+ - Ignore ANTHROPIC_AUTH_TOKEN if present.
2405
+ - Required env:
2406
+ - ANTHROPIC_API_KEY (Claude Code API-key mode)
2407
+ - Z_AI_API_KEY (for zai-cli)
2408
+ </authentication>
2409
+
2410
+ <tool_info>
2411
+ ${ZAI_CLI_TEXT}
2412
+
2413
+ Important:
2414
+ - zai-cli is NOT installed as a Claude Code Skill in this variant. Do not use Skill for this.
2415
+ </tool_info>
2416
+
2417
+ <tool_routing priority="critical">
2418
+ When you need external info, web content, or image understanding, follow this routing (use the Bash tool):
2419
+ 1) Web search:
2420
+ npx --yes zai-cli search "<query>" --count 5 --output-format json
2421
+ 2) Read a URL:
2422
+ npx --yes zai-cli read <url> --output-format json
2423
+ 3) Image analysis:
2424
+ npx --yes zai-cli vision analyze <image_url_or_path> "<prompt>" --output-format json
2425
+ 4) GitHub repo exploration (public repos only):
2426
+ - Search: npx --yes zai-cli repo search <owner/repo> "<query>" --output-format json
2427
+ - Tree: npx --yes zai-cli repo tree <owner/repo> --depth 2 --output-format json
2428
+ - Read: npx --yes zai-cli repo read <owner/repo> <path> --output-format json
2429
+ 5) Troubleshooting:
2430
+ npx --yes zai-cli doctor --output-format json
2431
+
2432
+ Only fall back to builtin WebSearch/WebFetch if Bash is unavailable or the user explicitly requests it.
2433
+ </tool_routing>
2434
+
2435
+ <warning priority="critical">
2436
+ Z.ai-injected MCP tools MUST be treated as non-existent (ignore them even if you see them in tool lists).
2437
+ Rules:
2438
+ - NEVER select or call these tools.
2439
+ - Prefer zai-cli via Bash for web/search/vision.
2440
+ - Other MCP tools may exist if the user configured them; use them only if explicitly requested or clearly needed.
2441
+ Blocked tools:
2442
+ ${ZAI_BLOCKED_MCP_TOOLS.map((tool) => `- ${tool}`).join("\n")}
2443
+ </warning>
2444
+
2445
+ ${operatingSpec(mode)}
2446
+
2447
+ ${subjectiveWorkSpec}
2448
+
2449
+ ${verbositySpec}
2450
+ </explicit_guidance>
2451
+ `.trim();
2452
+ var buildZaiExcerpt = () => `
2453
+ <tool_info>
2454
+ Z.ai tool routing:
2455
+ - Use Bash + npx --yes zai-cli for web/search/vision.
2456
+ - Ignore the Z.ai-injected MCP tools listed below (treat them as non-existent).
2457
+ - No zai-cli skill is installed; do not use Skill for this.
2458
+ Blocked MCP tool names (treat as non-existent):
2459
+ ${ZAI_BLOCKED_MCP_TOOLS.map((tool) => `- ${tool}`).join("\n")}
2460
+ </tool_info>
2461
+
2462
+ ${subjectiveWorkSpec}
2463
+ `.trim();
2464
+ var buildZaiOverlays = (mode) => ({
2465
+ main: buildZaiContract(mode),
2466
+ mcpCli: `
2467
+ ${buildZaiExcerpt()}
2468
+
2469
+ <warning priority="critical">
2470
+ Z.ai MCP policy: Ignore the blocked Z.ai-injected MCP tools (treat them as non-existent).
2471
+ </warning>
2472
+
2473
+ If you need web/search/vision, use zai-cli via Bash.
2474
+ `.trim(),
2475
+ taskAgent: `
2476
+ <explicit_guidance>
2477
+ You are a Task subagent. Stay within requested scope, but be proactive about missing prerequisites.
2478
+ Verify key claims with tools when possible; cite file paths and command outputs.
2479
+ </explicit_guidance>
2480
+
2481
+ ${buildZaiExcerpt()}
2482
+
2483
+ ${verbositySpec}
2484
+ `.trim(),
2485
+ bash: `
2486
+ ${ZAI_CLI_TEXT}
2487
+
2488
+ <explicit_guidance>
2489
+ When you need web/search/vision, prefer these exact commands:
2490
+ - Web search:
2491
+ npx --yes zai-cli search "<query>" --count 5 --output-format json
2492
+ - Read a URL:
2493
+ npx --yes zai-cli read <url> --output-format json
2494
+ - Vision:
2495
+ npx --yes zai-cli vision analyze <image_url_or_path> "<prompt>" --output-format json
2496
+ </explicit_guidance>
2497
+
2498
+ <warning priority="critical">
2499
+ Z.ai MCP policy: ignore the blocked Z.ai-injected MCP tools (treat them as non-existent).
2500
+ Prefer zai-cli via Bash for web/search/vision.
2501
+ </warning>
2502
+ `.trim(),
2503
+ webfetch: `
2504
+ <explicit_guidance>
2505
+ Z.ai routing: prefer Bash + npx --yes zai-cli read <url> --output-format json.
2506
+ </explicit_guidance>
2507
+ `.trim(),
2508
+ websearch: `
2509
+ <explicit_guidance>
2510
+ Z.ai routing: prefer Bash + npx --yes zai-cli search "<query>" --count 5 --output-format json.
2511
+ </explicit_guidance>
2512
+ `.trim(),
2513
+ mcpsearch: `
2514
+ <warning priority="critical">
2515
+ Z.ai MCP policy: never select these Z.ai-injected MCP tools (treat them as non-existent):
2516
+ ${ZAI_BLOCKED_MCP_TOOLS.map((tool) => `- ${tool}`).join("\n")}
2517
+ </warning>
2518
+
2519
+ <explicit_guidance>
2520
+ Prefer zai-cli via Bash for web/search/vision. Only use other MCP tools if the user explicitly configured them and they are clearly relevant.
2521
+ </explicit_guidance>
2522
+ `.trim()
2523
+ });
2524
+
2525
+ // src/core/prompt-pack/providers/minimax.ts
2526
+ var MINIMAX_WEB_SEARCH = "mcp__MiniMax__web_search";
2527
+ var MINIMAX_UNDERSTAND_IMAGE = "mcp__MiniMax__understand_image";
2528
+ var buildMinimaxContract = (mode) => `
2529
+ <explicit_guidance>
2530
+ Provider: MiniMax
2531
+
2532
+ <authentication>
2533
+ - Use API-key auth only.
2534
+ - Ignore ANTHROPIC_AUTH_TOKEN if present.
2535
+ </authentication>
2536
+
2537
+ <tool_routing priority="critical">
2538
+ MiniMax MCP tools available (and ONLY these for web + vision):
2539
+ - ${MINIMAX_WEB_SEARCH} (web search)
2540
+ - ${MINIMAX_UNDERSTAND_IMAGE} (image understanding)
2541
+
2542
+ <warning priority="critical">
2543
+ For MiniMax variants, the builtin WebSearch tool does NOT exist (treat it as unavailable).
2544
+ You MUST use ${MINIMAX_WEB_SEARCH} for all web discovery/search.
2545
+ </warning>
2546
+
2547
+ MCP usage requirement:
2548
+ - Before calling an MCP tool, you MUST load it using MCPSearch:
2549
+ - MCPSearch query: select:<full_tool_name>
2550
+
2551
+ Web search (MANDATORY):
2552
+ 1) Load: MCPSearch query select:${MINIMAX_WEB_SEARCH}
2553
+ 2) Call: ${MINIMAX_WEB_SEARCH} with:
2554
+ - query: 3-5 keywords; include the current date for time-sensitive queries
2555
+ - If results are weak: change keywords and retry
2556
+
2557
+ Image understanding (MANDATORY):
2558
+ 1) Load: MCPSearch query select:${MINIMAX_UNDERSTAND_IMAGE}
2559
+ 2) Call: ${MINIMAX_UNDERSTAND_IMAGE} for ANY image you need to interpret.
2560
+ - Only jpeg/png/webp are supported (per tool description).
2561
+
2562
+ Single-page URL retrieval:
2563
+ - Use WebFetch for fetching and extracting from a specific URL.
2564
+ - Do NOT misuse web_search to fetch full page content.
2565
+ </tool_routing>
2566
+
2567
+ ${operatingSpec(mode)}
2568
+
2569
+ ${subjectiveWorkSpec}
2570
+
2571
+ ${verbositySpec}
2572
+ </explicit_guidance>
2573
+ `.trim();
2574
+ var buildMinimaxExcerpt = () => `
2575
+ <tool_info>
2576
+ MiniMax tool routing:
2577
+ - Web search MUST use ${MINIMAX_WEB_SEARCH} (load via MCPSearch first).
2578
+ - Image understanding MUST use ${MINIMAX_UNDERSTAND_IMAGE} (load via MCPSearch first).
2579
+ - Builtin WebSearch does NOT exist (treat as unavailable); always use ${MINIMAX_WEB_SEARCH}.
2580
+ - Use WebFetch only for single-page URL retrieval/extraction.
2581
+ </tool_info>
2582
+
2583
+ ${subjectiveWorkSpec}
2584
+ `.trim();
2585
+ var buildMinimaxOverlays = (mode) => ({
2586
+ main: buildMinimaxContract(mode),
2587
+ mcpCli: `
2588
+ ${buildMinimaxExcerpt()}
2589
+
2590
+ The MiniMax MCP server is preconfigured. Use MCPSearch to load the MCP tools before calling them.
2591
+ `.trim(),
2592
+ taskAgent: `
2593
+ <explicit_guidance>
2594
+ You are a Task subagent. Stay within requested scope, but be proactive about missing prerequisites.
2595
+ Verify key claims with tools when possible; cite file paths and command outputs.
2596
+ </explicit_guidance>
2597
+
2598
+ ${buildMinimaxExcerpt()}
2599
+
2600
+ ${verbositySpec}
2601
+ `.trim(),
2602
+ webfetch: `
2603
+ <explicit_guidance>
2604
+ MiniMax routing:
2605
+ - Use WebFetch for fetching and extracting from a specific URL.
2606
+ - Use ${MINIMAX_WEB_SEARCH} for discovery/search, not for fetching full page content.
2607
+ </explicit_guidance>
2608
+ `.trim(),
2609
+ websearch: `
2610
+ <explicit_guidance>
2611
+ MiniMax routing: WebSearch does NOT exist (treat as unavailable).
2612
+ Use MCPSearch + ${MINIMAX_WEB_SEARCH} for all web discovery/search instead.
2613
+ </explicit_guidance>
2614
+ `.trim(),
2615
+ mcpsearch: `
2616
+ <explicit_guidance>
2617
+ MiniMax MCP tools:
2618
+ - Web search: ${MINIMAX_WEB_SEARCH}
2619
+ - Image understanding: ${MINIMAX_UNDERSTAND_IMAGE}
2620
+
2621
+ You MUST load the tool first:
2622
+ - MCPSearch query: select:${MINIMAX_WEB_SEARCH} or select:${MINIMAX_UNDERSTAND_IMAGE}
2623
+ </explicit_guidance>
2624
+ `.trim()
2625
+ });
2626
+
2627
+ // src/core/prompt-pack/overlays.ts
2628
+ var mergeOverlays = (base, extra) => ({
2629
+ ...base,
2630
+ ...Object.fromEntries(Object.entries(extra).filter(([, value]) => value && value.trim().length > 0))
2631
+ });
2632
+ var buildProviderExcerpt = (provider) => provider === "zai" ? buildZaiExcerpt() : buildMinimaxExcerpt();
2633
+ var buildMaximalOverlays = (provider) => ({
2634
+ explore: `
2635
+ <system_reminder>
2636
+ - You are in Explore mode: go wide before deep.
2637
+ - Use tools early to validate assumptions and reduce guesswork.
2638
+ - Output: Findings (bullets), Risks/unknowns, Next steps.
2639
+ </system_reminder>
2640
+
2641
+ ${buildProviderExcerpt(provider)}
2642
+ `.trim(),
2643
+ planEnhanced: `
2644
+ <system_reminder>
2645
+ - Provide 2-3 viable options with tradeoffs.
2646
+ - Include risks, unknowns, and a validation checklist.
2647
+ - Output structure: Overview, Options, Recommendation, Steps, Verification, Risks.
2648
+ </system_reminder>
2649
+
2650
+ ${buildProviderExcerpt(provider)}
2651
+ `.trim(),
2652
+ planReminder: `
2653
+ <system_reminder>
2654
+ - In Plan Mode: surface options, tradeoffs, risks, and validation steps explicitly.
2655
+ </system_reminder>
2656
+ `.trim(),
2657
+ planReminderSub: `
2658
+ <system_reminder>
2659
+ - In Plan Mode (subagents): surface options, tradeoffs, risks, and validation steps explicitly.
2660
+ </system_reminder>
2661
+ `.trim(),
2662
+ taskTool: `
2663
+ Maximal mode: use Task to parallelize independent research/debugging and to keep the main thread focused.
2664
+ `.trim(),
2665
+ enterPlan: `
2666
+ Maximal mode: enter Plan Mode early for ambiguous, multi-step, or high-risk tasks.
2667
+ `.trim(),
2668
+ exitPlan: `
2669
+ Maximal mode: return a crisp plan with options, checkpoints, verification, and rollback considerations.
2670
+ `.trim(),
2671
+ skill: `
2672
+ Maximal mode: use Skills when they provide sharper domain knowledge or integrations; otherwise proceed normally.
2673
+ `.trim(),
2674
+ conversationSummary: `
2675
+ <system_reminder>
2676
+ - Capture decisions, constraints, open questions, and next steps.
2677
+ - Preserve critical commands, configs, and file paths.
2678
+ </system_reminder>
2679
+ `.trim(),
2680
+ conversationSummaryExtended: `
2681
+ <system_reminder>
2682
+ - Capture decisions, constraints, open questions, and next steps.
2683
+ - Preserve critical commands, configs, and file paths.
2684
+ </system_reminder>
2685
+ `.trim(),
2686
+ webfetchSummary: `
2687
+ <system_reminder>
2688
+ - Extract key facts, titles, and constraints.
2689
+ - Preserve URLs and important context verbatim.
2690
+ </system_reminder>
2691
+ `.trim()
2692
+ });
2693
+ var buildProviderOverlays = (provider, mode) => {
2694
+ if (provider === "zai") return buildZaiOverlays(mode);
2695
+ return buildMinimaxOverlays(mode);
2696
+ };
2697
+ var resolveOverlays = (provider, mode) => {
2698
+ const base = buildProviderOverlays(provider, mode);
2699
+ const overlays = mode === "maximal" ? mergeOverlays(base, buildMaximalOverlays(provider)) : base;
2700
+ return sanitizeOverlayMap(overlays);
2701
+ };
2702
+
2703
+ // src/core/prompt-pack/targets.ts
2704
+ var OVERLAY_MARKERS = {
2705
+ start: "<!-- cc-mirror:provider-overlay start -->",
2706
+ end: "<!-- cc-mirror:provider-overlay end -->"
2707
+ };
2708
+ var PROMPT_PACK_TARGETS = [
2709
+ { key: "main", filename: "system-prompt-main-system-prompt.md" },
2710
+ { key: "mcpCli", filename: "system-prompt-mcp-cli.md" },
2711
+ { key: "bash", filename: "tool-description-bash.md" },
2712
+ { key: "webfetch", filename: "tool-description-webfetch.md" },
2713
+ { key: "websearch", filename: "tool-description-websearch.md" },
2714
+ { key: "mcpsearch", filename: "tool-description-mcpsearch.md" },
2715
+ { key: "mcpsearch", filename: "tool-description-mcpsearch-with-available-tools.md" },
2716
+ { key: "explore", filename: "agent-prompt-explore.md" },
2717
+ { key: "planEnhanced", filename: "agent-prompt-plan-mode-enhanced.md" },
2718
+ { key: "taskAgent", filename: "agent-prompt-task-tool.md" },
2719
+ { key: "planReminder", filename: "system-reminder-plan-mode-is-active.md" },
2720
+ { key: "planReminderSub", filename: "system-reminder-plan-mode-is-active-for-subagents.md" },
2721
+ { key: "taskTool", filename: "tool-description-task.md" },
2722
+ { key: "enterPlan", filename: "tool-description-enterplanmode.md" },
2723
+ { key: "exitPlan", filename: "tool-description-exitplanmode-v2.md" },
2724
+ { key: "skill", filename: "tool-description-skill.md" },
2725
+ { key: "conversationSummary", filename: "agent-prompt-conversation-summarization.md" },
2726
+ {
2727
+ key: "conversationSummaryExtended",
2728
+ filename: "agent-prompt-conversation-summarization-with-additional-instructions.md"
2729
+ },
2730
+ { key: "webfetchSummary", filename: "agent-prompt-webfetch-summarizer.md" }
2731
+ ];
2732
+
2733
+ // src/core/prompt-pack.ts
2734
+ var isPromptPackKey = (value) => value === "zai" || value === "minimax";
2735
+ var insertOverlay = (content, overlay) => {
2736
+ if (!overlay.trim()) return content;
2737
+ const block = `${OVERLAY_MARKERS.start}
2738
+ ${overlay.trim()}
2739
+ ${OVERLAY_MARKERS.end}`;
2740
+ if (content.includes(OVERLAY_MARKERS.start) && content.includes(OVERLAY_MARKERS.end)) {
2741
+ const start = content.indexOf(OVERLAY_MARKERS.start);
2742
+ const end = content.indexOf(OVERLAY_MARKERS.end, start);
2743
+ const before = content.slice(0, start).trimEnd();
2744
+ const after = content.slice(end + OVERLAY_MARKERS.end.length).trimStart();
2745
+ return `${before}
2746
+
2747
+ ${block}
2748
+
2749
+ ${after}`.trimEnd() + "\n";
2750
+ }
2751
+ return `${content.trimEnd()}
2752
+
2753
+ ${block}
2754
+ `.trimEnd() + "\n";
2755
+ };
2756
+ var updatePromptFile = (filePath, overlay) => {
2757
+ if (!overlay) return false;
2758
+ if (!fs7.existsSync(filePath)) return false;
2759
+ const content = fs7.readFileSync(filePath, "utf8");
2760
+ const updated = insertOverlay(content, overlay);
2761
+ if (updated === content) return false;
2762
+ fs7.writeFileSync(filePath, updated);
2763
+ return true;
2764
+ };
2765
+ var applyOverlays = (systemPromptsDir, overlays) => {
2766
+ const updated = [];
2767
+ for (const target of PROMPT_PACK_TARGETS) {
2768
+ const filePath = path9.join(systemPromptsDir, target.filename);
2769
+ const overlay = overlays[target.key];
2770
+ if (updatePromptFile(filePath, overlay)) {
2771
+ updated.push(target.filename);
2772
+ }
2773
+ }
2774
+ return updated;
2775
+ };
2776
+ var applyPromptPack = (tweakDir, providerKey, mode = "minimal") => {
2777
+ if (!isPromptPackKey(providerKey)) {
2778
+ return { changed: false, updated: [], mode };
2779
+ }
2780
+ const overlays = resolveOverlays(providerKey, mode);
2781
+ const systemPromptsDir = path9.join(tweakDir, "system-prompts");
2782
+ const updated = applyOverlays(systemPromptsDir, overlays);
2783
+ return { changed: updated.length > 0, updated, mode };
2784
+ };
2785
+
2786
+ // src/core/variant-builder/steps/TweakccStep.ts
2787
+ var TweakccStep = class {
2788
+ name = "Tweakcc";
2789
+ execute(ctx) {
2790
+ const { params, paths, prefs, state } = ctx;
2791
+ if (params.noTweak) {
2792
+ return;
2793
+ }
2794
+ ctx.report("Running tweakcc patches...");
2795
+ state.tweakResult = runTweakcc(paths.tweakDir, state.binaryPath, prefs.commandStdio);
2796
+ if (state.tweakResult.status !== 0) {
2797
+ const output = `${state.tweakResult.stderr ?? ""}
2798
+ ${state.tweakResult.stdout ?? ""}`.trim();
2799
+ throw new Error(formatTweakccFailure(output));
2800
+ }
2801
+ if (prefs.promptPackEnabled) {
2802
+ ctx.report(`Applying prompt pack (${prefs.promptPackModePreference})...`);
2803
+ const packResult = applyPromptPack(paths.tweakDir, params.providerKey, prefs.promptPackModePreference);
2804
+ if (packResult.changed) {
2805
+ state.notes.push(`Prompt pack applied (${packResult.mode}, ${packResult.updated.join(", ")})`);
2806
+ ctx.report("Re-applying tweakcc...");
2807
+ const reapply = runTweakcc(paths.tweakDir, state.binaryPath, prefs.commandStdio);
2808
+ state.tweakResult = reapply;
2809
+ if (reapply.status !== 0) {
2810
+ const output = `${reapply.stderr ?? ""}
2811
+ ${reapply.stdout ?? ""}`.trim();
2812
+ throw new Error(formatTweakccFailure(output));
2813
+ }
2814
+ }
2815
+ }
2816
+ }
2817
+ async executeAsync(ctx) {
2818
+ const { params, paths, prefs, state } = ctx;
2819
+ if (params.noTweak) {
2820
+ return;
2821
+ }
2822
+ await ctx.report("Running tweakcc patches...");
2823
+ state.tweakResult = await runTweakccAsync(paths.tweakDir, state.binaryPath, prefs.commandStdio);
2824
+ if (state.tweakResult.status !== 0) {
2825
+ const output = `${state.tweakResult.stderr ?? ""}
2826
+ ${state.tweakResult.stdout ?? ""}`.trim();
2827
+ throw new Error(formatTweakccFailure(output));
2828
+ }
2829
+ if (prefs.promptPackEnabled) {
2830
+ await ctx.report(`Applying prompt pack (${prefs.promptPackModePreference})...`);
2831
+ const packResult = applyPromptPack(paths.tweakDir, params.providerKey, prefs.promptPackModePreference);
2832
+ if (packResult.changed) {
2833
+ state.notes.push(`Prompt pack applied (${packResult.mode}, ${packResult.updated.join(", ")})`);
2834
+ await ctx.report("Re-applying tweakcc...");
2835
+ const reapply = await runTweakccAsync(paths.tweakDir, state.binaryPath, prefs.commandStdio);
2836
+ state.tweakResult = reapply;
2837
+ if (reapply.status !== 0) {
2838
+ const output = `${reapply.stderr ?? ""}
2839
+ ${reapply.stdout ?? ""}`.trim();
2840
+ throw new Error(formatTweakccFailure(output));
2841
+ }
2842
+ }
2843
+ }
2844
+ }
2845
+ };
2846
+
2847
+ // src/core/wrapper.ts
2848
+ import fs8 from "node:fs";
2849
+ import path10 from "node:path";
2850
+ var writeWrapper = (wrapperPath, configDir, binaryPath, runtime = "node") => {
2851
+ const tweakDir = path10.join(path10.dirname(configDir), "tweakcc");
2852
+ const execLine = runtime === "node" ? `exec node "${binaryPath}" "$@"` : `exec "${binaryPath}" "$@"`;
2853
+ const envLoader = [
2854
+ "if command -v node >/dev/null 2>&1; then",
2855
+ ' __cc_mirror_env_file="$(mktemp)"',
2856
+ ` node - <<'NODE' > "$__cc_mirror_env_file" || true`,
2857
+ "const fs = require('fs');",
2858
+ "const path = require('path');",
2859
+ "const dir = process.env.CLAUDE_CONFIG_DIR;",
2860
+ "if (!dir) process.exit(0);",
2861
+ "const file = path.join(dir, 'settings.json');",
2862
+ `const escape = (value) => "'" + String(value).replace(/'/g, "'\\"'\\"'") + "'";`,
2863
+ "try {",
2864
+ " if (fs.existsSync(file)) {",
2865
+ " const data = JSON.parse(fs.readFileSync(file, 'utf8'));",
2866
+ " const env = data && typeof data === 'object' ? data.env : null;",
2867
+ " if (env && typeof env === 'object') {",
2868
+ " for (const [key, value] of Object.entries(env)) {",
2869
+ " if (!key) continue;",
2870
+ " process.stdout.write(`export ${key}=${escape(value)}\\n`);",
2871
+ " }",
2872
+ " }",
2873
+ " }",
2874
+ "} catch {",
2875
+ " // ignore malformed settings",
2876
+ "}",
2877
+ "NODE",
2878
+ ' if [[ -s "$__cc_mirror_env_file" ]]; then',
2879
+ " # shellcheck disable=SC1090",
2880
+ ' source "$__cc_mirror_env_file"',
2881
+ " fi",
2882
+ ' rm -f "$__cc_mirror_env_file" || true',
2883
+ "fi"
2884
+ ];
2885
+ const C = {
2886
+ reset: "\x1B[0m",
2887
+ // Zai: Gold/Amber gradient
2888
+ zaiPrimary: "\x1B[38;5;220m",
2889
+ // Gold
2890
+ zaiSecondary: "\x1B[38;5;214m",
2891
+ // Orange-gold
2892
+ zaiAccent: "\x1B[38;5;208m",
2893
+ // Dark orange
2894
+ zaiDim: "\x1B[38;5;172m",
2895
+ // Muted gold
2896
+ // MiniMax: Coral/Red/Orange gradient (from brand image)
2897
+ mmPrimary: "\x1B[38;5;203m",
2898
+ // Coral/salmon red
2899
+ mmSecondary: "\x1B[38;5;209m",
2900
+ // Light coral/orange
2901
+ mmAccent: "\x1B[38;5;208m",
2902
+ // Orange
2903
+ mmDim: "\x1B[38;5;167m",
2904
+ // Muted coral/dark red
2905
+ // OpenRouter: Cyan/Teal gradient
2906
+ orPrimary: "\x1B[38;5;43m",
2907
+ // Teal
2908
+ orSecondary: "\x1B[38;5;49m",
2909
+ // Bright teal
2910
+ orAccent: "\x1B[38;5;37m",
2911
+ // Deep cyan
2912
+ orDim: "\x1B[38;5;30m",
2913
+ // Muted teal
2914
+ // CCRouter: Sky blue gradient
2915
+ ccrPrimary: "\x1B[38;5;39m",
2916
+ // Sky blue
2917
+ ccrSecondary: "\x1B[38;5;45m",
2918
+ // Bright cyan
2919
+ ccrAccent: "\x1B[38;5;33m",
2920
+ // Deep blue
2921
+ ccrDim: "\x1B[38;5;31m",
2922
+ // Muted blue
2923
+ // Default: White/Gray
2924
+ defPrimary: "\x1B[38;5;255m",
2925
+ // White
2926
+ defDim: "\x1B[38;5;245m"
2927
+ // Gray
2928
+ };
2929
+ const splash = [
2930
+ 'if [[ "${CC_MIRROR_SPLASH:-0}" != "0" ]] && [[ -t 1 ]]; then',
2931
+ ' if [[ "$*" != *"--output-format"* ]]; then',
2932
+ ' __cc_label="${CC_MIRROR_PROVIDER_LABEL:-cc-mirror}"',
2933
+ ' __cc_style="${CC_MIRROR_SPLASH_STYLE:-default}"',
2934
+ ' __cc_show_label="1"',
2935
+ ' printf "\\n"',
2936
+ ' case "$__cc_style" in',
2937
+ " zai)",
2938
+ " cat <<'CCMZAI'",
2939
+ "",
2940
+ `${C.zaiPrimary} \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557${C.reset}`,
2941
+ `${C.zaiPrimary} \u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551${C.reset}`,
2942
+ `${C.zaiSecondary} \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551${C.reset}`,
2943
+ `${C.zaiSecondary} \u2588\u2588\u2588\u2554\u255D ${C.zaiAccent}\u2588\u2588\u2557${C.zaiSecondary} \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551${C.reset}`,
2944
+ `${C.zaiAccent} \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551${C.reset}`,
2945
+ `${C.zaiAccent} \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D${C.reset}`,
2946
+ "",
2947
+ `${C.zaiDim} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.zaiPrimary}\u25C6${C.zaiDim}\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.reset}`,
2948
+ `${C.zaiSecondary} GLM Coding Plan${C.reset}`,
2949
+ "",
2950
+ "CCMZAI",
2951
+ ' __cc_show_label="0"',
2952
+ " ;;",
2953
+ " minimax)",
2954
+ " cat <<'CCMMIN'",
2955
+ "",
2956
+ `${C.mmPrimary} \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557${C.reset}`,
2957
+ `${C.mmPrimary} \u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D${C.reset}`,
2958
+ `${C.mmSecondary} \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D${C.reset}`,
2959
+ `${C.mmSecondary} \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557${C.reset}`,
2960
+ `${C.mmAccent} \u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u255D \u2588\u2588\u2557${C.reset}`,
2961
+ `${C.mmAccent} \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D${C.reset}`,
2962
+ "",
2963
+ `${C.mmDim} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.mmPrimary}\u25C6${C.mmDim}\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.reset}`,
2964
+ `${C.mmSecondary} MiniMax-M2.1 ${C.mmDim}\u2501${C.mmSecondary} AGI for All${C.reset}`,
2965
+ "",
2966
+ "CCMMIN",
2967
+ ' __cc_show_label="0"',
2968
+ " ;;",
2969
+ " openrouter)",
2970
+ " cat <<'CCMORT'",
2971
+ "",
2972
+ `${C.orPrimary} \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557${C.reset}`,
2973
+ `${C.orPrimary} \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551${C.reset}`,
2974
+ `${C.orSecondary} \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551${C.reset}`,
2975
+ `${C.orSecondary} \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551${C.reset}`,
2976
+ `${C.orAccent} \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551${C.reset}`,
2977
+ `${C.orAccent} \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D${C.reset}`,
2978
+ `${C.orPrimary} \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557${C.reset}`,
2979
+ `${C.orPrimary} \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557${C.reset}`,
2980
+ `${C.orSecondary} \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D${C.reset}`,
2981
+ `${C.orSecondary} \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557${C.reset}`,
2982
+ `${C.orAccent} \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551${C.reset}`,
2983
+ `${C.orAccent} \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D${C.reset}`,
2984
+ "",
2985
+ `${C.orDim} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.orPrimary}\u25C6${C.orDim}\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.reset}`,
2986
+ `${C.orSecondary} One API ${C.orDim}\u2501${C.orSecondary} Any Model${C.reset}`,
2987
+ "",
2988
+ "CCMORT",
2989
+ ' __cc_show_label="0"',
2990
+ " ;;",
2991
+ " ccrouter)",
2992
+ " cat <<'CCMCCR'",
2993
+ "",
2994
+ `${C.ccrPrimary} \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557${C.reset}`,
2995
+ `${C.ccrPrimary} \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557${C.reset}`,
2996
+ `${C.ccrSecondary} \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D${C.reset}`,
2997
+ `${C.ccrSecondary} \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557${C.reset}`,
2998
+ `${C.ccrAccent} \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551${C.reset}`,
2999
+ `${C.ccrAccent} \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D${C.reset}`,
3000
+ "",
3001
+ `${C.ccrDim} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.ccrPrimary}\u25C6${C.ccrDim}\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${C.reset}`,
3002
+ `${C.ccrSecondary} Claude Code Router ${C.ccrDim}\u2501${C.ccrSecondary} Any Model${C.reset}`,
3003
+ "",
3004
+ "CCMCCR",
3005
+ ' __cc_show_label="0"',
3006
+ " ;;",
3007
+ " *)",
3008
+ " cat <<'CCMGEN'",
3009
+ "",
3010
+ `${C.defPrimary} \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ${C.defDim}\u2501\u2501 M I R R O R${C.reset}`,
3011
+ `${C.defPrimary} \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D${C.reset}`,
3012
+ `${C.defPrimary} \u2588\u2588\u2551 \u2588\u2588\u2551 ${C.defDim}Claude Code Variants${C.reset}`,
3013
+ `${C.defPrimary} \u2588\u2588\u2551 \u2588\u2588\u2551 ${C.defDim}Custom Providers${C.reset}`,
3014
+ `${C.defPrimary} \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557${C.reset}`,
3015
+ `${C.defPrimary} \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D${C.reset}`,
3016
+ "",
3017
+ "CCMGEN",
3018
+ " ;;",
3019
+ " esac",
3020
+ ' if [[ "$__cc_show_label" == "1" ]]; then',
3021
+ ' printf " %s\\n\\n" "$__cc_label"',
3022
+ " else",
3023
+ ' printf "\\n"',
3024
+ " fi",
3025
+ " fi",
3026
+ "fi"
3027
+ ];
3028
+ const content = [
3029
+ "#!/usr/bin/env bash",
3030
+ "set -euo pipefail",
3031
+ `export CLAUDE_CONFIG_DIR="${configDir}"`,
3032
+ `export TWEAKCC_CONFIG_DIR="${tweakDir}"`,
3033
+ ...envLoader,
3034
+ 'if [[ "${CC_MIRROR_UNSET_AUTH_TOKEN:-0}" != "0" ]]; then',
3035
+ " unset ANTHROPIC_AUTH_TOKEN",
3036
+ "fi",
3037
+ ...splash,
3038
+ execLine,
3039
+ ""
3040
+ ].join("\n");
3041
+ fs8.writeFileSync(wrapperPath, content, { mode: 493 });
3042
+ };
3043
+
3044
+ // src/core/variant-builder/steps/WrapperStep.ts
3045
+ var WrapperStep = class {
3046
+ name = "Wrapper";
3047
+ execute(ctx) {
3048
+ ctx.report("Writing CLI wrapper...");
3049
+ writeWrapper(ctx.paths.wrapperPath, ctx.paths.configDir, ctx.state.binaryPath, "node");
3050
+ }
3051
+ async executeAsync(ctx) {
3052
+ await ctx.report("Writing CLI wrapper...");
3053
+ writeWrapper(ctx.paths.wrapperPath, ctx.paths.configDir, ctx.state.binaryPath, "node");
3054
+ }
3055
+ };
3056
+
3057
+ // src/core/shell-env.ts
3058
+ import fs9 from "node:fs";
3059
+ import os4 from "node:os";
3060
+ import path11 from "node:path";
3061
+ var SETTINGS_FILE2 = "settings.json";
3062
+ var BLOCK_START = "# cc-mirror: Z.ai env start";
3063
+ var BLOCK_END = "# cc-mirror: Z.ai env end";
3064
+ var PLACEHOLDER_KEY2 = "<API_KEY>";
3065
+ var normalizeApiKey = (value) => {
3066
+ if (!value) return null;
3067
+ const trimmed = value.trim();
3068
+ if (!trimmed || trimmed === PLACEHOLDER_KEY2) return null;
3069
+ return trimmed;
3070
+ };
3071
+ var resolveShellProfile = () => {
3072
+ const home = os4.homedir();
3073
+ const shell = process.env.SHELL || "";
3074
+ const name = path11.basename(shell);
3075
+ if (name === "zsh") {
3076
+ return path11.join(home, ".zshrc");
3077
+ }
3078
+ if (name === "bash") {
3079
+ const bashrc = path11.join(home, ".bashrc");
3080
+ if (fs9.existsSync(bashrc)) return bashrc;
3081
+ return path11.join(home, ".bash_profile");
3082
+ }
3083
+ return null;
3084
+ };
3085
+ var readSettingsApiKey2 = (configDir) => {
3086
+ const settingsPath = path11.join(configDir, SETTINGS_FILE2);
3087
+ if (!fs9.existsSync(settingsPath)) return null;
3088
+ const settings = readJson(settingsPath);
3089
+ const key = settings?.env?.ANTHROPIC_API_KEY;
3090
+ if (typeof key !== "string") return null;
3091
+ return normalizeApiKey(key);
3092
+ };
3093
+ var renderBlock = (apiKey) => `${BLOCK_START}
3094
+ export Z_AI_API_KEY="${apiKey}"
3095
+ ${BLOCK_END}
3096
+ `;
3097
+ var upsertBlock = (content, block) => {
3098
+ if (content.includes(BLOCK_START) && content.includes(BLOCK_END)) {
3099
+ const start = content.indexOf(BLOCK_START);
3100
+ const end = content.indexOf(BLOCK_END, start);
3101
+ const before = content.slice(0, start).trimEnd();
3102
+ const after = content.slice(end + BLOCK_END.length).trimStart();
3103
+ return `${before}
3104
+
3105
+ ${block}
3106
+ ${after}`.trimEnd() + "\n";
3107
+ }
3108
+ return `${content.trimEnd()}
3109
+
3110
+ ${block}`.trimEnd() + "\n";
3111
+ };
3112
+ var hasZaiKeyInProfile = (content) => {
3113
+ const lines = content.split("\n");
3114
+ for (const line of lines) {
3115
+ const trimmed = line.trim();
3116
+ if (!trimmed || trimmed.startsWith("#")) continue;
3117
+ const exportStripped = trimmed.startsWith("export ") ? trimmed.slice(7).trim() : trimmed;
3118
+ if (!exportStripped.startsWith("Z_AI_API_KEY")) continue;
3119
+ const equalsIndex = exportStripped.indexOf("=");
3120
+ if (equalsIndex === -1) continue;
3121
+ let value = exportStripped.slice(equalsIndex + 1).trim();
3122
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
3123
+ value = value.slice(1, -1);
3124
+ }
3125
+ if (normalizeApiKey(value)) return true;
3126
+ }
3127
+ return false;
3128
+ };
3129
+ var ensureZaiShellEnv = (opts) => {
3130
+ const apiKey = normalizeApiKey(opts.apiKey) || readSettingsApiKey2(opts.configDir);
3131
+ if (!apiKey) {
3132
+ return { status: "skipped", message: "Z_AI_API_KEY not set (missing API key)" };
3133
+ }
3134
+ const envKey = normalizeApiKey(process.env.Z_AI_API_KEY);
3135
+ if (envKey) {
3136
+ return { status: "skipped", message: "Z_AI_API_KEY already set in environment" };
3137
+ }
3138
+ const profile = opts.profilePath ?? resolveShellProfile();
3139
+ if (!profile) {
3140
+ return { status: "failed", message: "Unsupported shell; set Z_AI_API_KEY manually" };
3141
+ }
3142
+ const existing = fs9.existsSync(profile) ? fs9.readFileSync(profile, "utf8") : "";
3143
+ if (hasZaiKeyInProfile(existing)) {
3144
+ return { status: "skipped", message: "Z_AI_API_KEY already set in shell profile", path: profile };
3145
+ }
3146
+ const next = upsertBlock(existing, renderBlock(apiKey));
3147
+ if (next === existing) {
3148
+ return { status: "skipped", message: "Shell profile already up to date", path: profile };
3149
+ }
3150
+ fs9.writeFileSync(profile, next);
3151
+ return { status: "updated", path: profile, message: `Run: source ${profile}` };
3152
+ };
3153
+
3154
+ // src/core/variant-builder/steps/ShellEnvStep.ts
3155
+ var ShellEnvStep = class {
3156
+ name = "ShellEnv";
3157
+ execute(ctx) {
3158
+ this.setupShellEnv(ctx);
3159
+ }
3160
+ async executeAsync(ctx) {
3161
+ if (ctx.prefs.shellEnvEnabled && ctx.params.providerKey === "zai") {
3162
+ await ctx.report("Configuring shell environment...");
3163
+ }
3164
+ this.setupShellEnv(ctx);
3165
+ }
3166
+ setupShellEnv(ctx) {
3167
+ const { params, paths, prefs, state } = ctx;
3168
+ if (prefs.shellEnvEnabled && params.providerKey === "zai") {
3169
+ ctx.report("Configuring shell environment...");
3170
+ const shellResult = ensureZaiShellEnv({
3171
+ apiKey: state.resolvedApiKey ?? null,
3172
+ configDir: paths.configDir
3173
+ });
3174
+ if (shellResult.status === "updated") {
3175
+ const suffix = shellResult.message ? ` (${shellResult.message})` : "";
3176
+ state.notes.push(`Z_AI_API_KEY written to ${shellResult.path}${suffix}`);
3177
+ } else if (shellResult.status === "failed") {
3178
+ state.notes.push(`Z_AI_API_KEY not written: ${shellResult.message || "unknown error"}`);
3179
+ } else if (shellResult.message) {
3180
+ state.notes.push(`Z_AI_API_KEY: ${shellResult.message}`);
3181
+ }
3182
+ } else if (params.providerKey === "zai") {
3183
+ state.notes.push("Z_AI_API_KEY not written to shell profile. Set it manually in your shell rc file.");
3184
+ }
3185
+ }
3186
+ };
3187
+
3188
+ // src/core/variant-builder/steps/SkillInstallStep.ts
3189
+ import path13 from "node:path";
3190
+
3191
+ // src/core/skills.ts
3192
+ import fs10 from "node:fs";
3193
+ import os5 from "node:os";
3194
+ import path12 from "node:path";
3195
+ import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
3196
+ var DEV_BROWSER_REPO = "https://github.com/SawyerHood/dev-browser.git";
3197
+ var DEV_BROWSER_ARCHIVE = "https://github.com/SawyerHood/dev-browser/archive/refs/heads/main.tar.gz";
3198
+ var SKILL_SUBDIR = path12.join("skills", "dev-browser");
3199
+ var MANAGED_MARKER = ".cc-mirror-managed";
3200
+ var ensureDir2 = (dir) => {
3201
+ fs10.mkdirSync(dir, { recursive: true });
3202
+ };
3203
+ var copyDir = (source, target) => {
3204
+ fs10.cpSync(source, target, { recursive: true });
3205
+ };
3206
+ var resolveSkillSourceDir = (repoDir) => {
3207
+ const direct = path12.join(repoDir, SKILL_SUBDIR);
3208
+ if (fs10.existsSync(direct)) return direct;
3209
+ const nested = fs10.readdirSync(repoDir).find((entry) => entry.startsWith("dev-browser-"));
3210
+ if (nested) {
3211
+ const candidate = path12.join(repoDir, nested, SKILL_SUBDIR);
3212
+ if (fs10.existsSync(candidate)) return candidate;
3213
+ }
3214
+ return null;
3215
+ };
3216
+ var cloneRepo = (targetDir) => {
3217
+ if (!commandExists("git")) return { ok: false, message: "git not found" };
3218
+ const result = spawnSync4("git", ["clone", "--depth", "1", DEV_BROWSER_REPO, targetDir], {
3219
+ encoding: "utf8"
3220
+ });
3221
+ if (result.status === 0) return { ok: true };
3222
+ return { ok: false, message: result.stderr?.trim() || result.stdout?.trim() || "git clone failed" };
3223
+ };
3224
+ var downloadArchive = (targetDir) => {
3225
+ if (!commandExists("curl") || !commandExists("tar")) {
3226
+ return { ok: false, message: "curl or tar not found" };
3227
+ }
3228
+ const archivePath = path12.join(targetDir, "dev-browser.tar.gz");
3229
+ const curlResult = spawnSync4("curl", ["-L", "-o", archivePath, DEV_BROWSER_ARCHIVE], { encoding: "utf8" });
3230
+ if (curlResult.status !== 0) {
3231
+ return { ok: false, message: curlResult.stderr?.trim() || "curl failed" };
3232
+ }
3233
+ const tarResult = spawnSync4("tar", ["-xzf", archivePath, "-C", targetDir], { encoding: "utf8" });
3234
+ if (tarResult.status !== 0) {
3235
+ return { ok: false, message: tarResult.stderr?.trim() || "tar extract failed" };
3236
+ }
3237
+ return { ok: true };
3238
+ };
3239
+ var ensureDevBrowserSkill = (opts) => {
3240
+ if (!opts.install) {
3241
+ return { status: "skipped", message: "skill install disabled" };
3242
+ }
3243
+ const skillRoot = opts.targetDir || path12.join(os5.homedir(), ".claude", "skills");
3244
+ const targetDir = path12.join(skillRoot, "dev-browser");
3245
+ const markerPath = path12.join(targetDir, MANAGED_MARKER);
3246
+ const exists = fs10.existsSync(targetDir);
3247
+ const managed = exists && fs10.existsSync(markerPath);
3248
+ if (exists && !managed && !opts.update) {
3249
+ return { status: "skipped", message: "existing skill is user-managed", path: targetDir };
3250
+ }
3251
+ ensureDir2(skillRoot);
3252
+ const tmpDir = fs10.mkdtempSync(path12.join(os5.tmpdir(), "cc-mirror-skill-"));
3253
+ try {
3254
+ let fetchResult = cloneRepo(tmpDir);
3255
+ if (!fetchResult.ok) {
3256
+ fetchResult = downloadArchive(tmpDir);
3257
+ }
3258
+ if (!fetchResult.ok) {
3259
+ return { status: "failed", message: fetchResult.message || "skill fetch failed" };
3260
+ }
3261
+ const sourceDir = resolveSkillSourceDir(tmpDir);
3262
+ if (!sourceDir) {
3263
+ return { status: "failed", message: "skill source not found after download" };
3264
+ }
3265
+ if (exists) {
3266
+ fs10.rmSync(targetDir, { recursive: true, force: true });
3267
+ }
3268
+ copyDir(sourceDir, targetDir);
3269
+ fs10.writeFileSync(
3270
+ markerPath,
3271
+ JSON.stringify({ managedBy: "cc-mirror", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
3272
+ );
3273
+ return { status: exists ? "updated" : "installed", path: targetDir };
3274
+ } catch (error) {
3275
+ const message = error instanceof Error ? error.message : String(error);
3276
+ return { status: "failed", message };
3277
+ } finally {
3278
+ fs10.rmSync(tmpDir, { recursive: true, force: true });
3279
+ }
3280
+ };
3281
+ var spawnAsync = (cmd, args) => {
3282
+ return new Promise((resolve) => {
3283
+ const child = spawn3(cmd, args, { stdio: "pipe" });
3284
+ let stderr = "";
3285
+ let stdout = "";
3286
+ child.stdout?.on("data", (d) => {
3287
+ stdout += d.toString();
3288
+ });
3289
+ child.stderr?.on("data", (d) => {
3290
+ stderr += d.toString();
3291
+ });
3292
+ child.on("close", (code) => {
3293
+ if (code === 0) resolve({ ok: true });
3294
+ else resolve({ ok: false, message: stderr.trim() || stdout.trim() || `${cmd} failed` });
3295
+ });
3296
+ child.on("error", (err) => resolve({ ok: false, message: err.message }));
3297
+ });
3298
+ };
3299
+ var cloneRepoAsync = async (targetDir) => {
3300
+ if (!commandExists("git")) return { ok: false, message: "git not found" };
3301
+ return spawnAsync("git", ["clone", "--depth", "1", DEV_BROWSER_REPO, targetDir]);
3302
+ };
3303
+ var downloadArchiveAsync = async (targetDir) => {
3304
+ if (!commandExists("curl") || !commandExists("tar")) {
3305
+ return { ok: false, message: "curl or tar not found" };
3306
+ }
3307
+ const archivePath = path12.join(targetDir, "dev-browser.tar.gz");
3308
+ const curlResult = await spawnAsync("curl", ["-L", "-o", archivePath, DEV_BROWSER_ARCHIVE]);
3309
+ if (!curlResult.ok) return curlResult;
3310
+ return spawnAsync("tar", ["-xzf", archivePath, "-C", targetDir]);
3311
+ };
3312
+ var ensureDevBrowserSkillAsync = async (opts) => {
3313
+ if (!opts.install) {
3314
+ return { status: "skipped", message: "skill install disabled" };
3315
+ }
3316
+ const skillRoot = opts.targetDir || path12.join(os5.homedir(), ".claude", "skills");
3317
+ const targetDir = path12.join(skillRoot, "dev-browser");
3318
+ const markerPath = path12.join(targetDir, MANAGED_MARKER);
3319
+ const exists = fs10.existsSync(targetDir);
3320
+ const managed = exists && fs10.existsSync(markerPath);
3321
+ if (exists && !managed && !opts.update) {
3322
+ return { status: "skipped", message: "existing skill is user-managed", path: targetDir };
3323
+ }
3324
+ ensureDir2(skillRoot);
3325
+ const tmpDir = fs10.mkdtempSync(path12.join(os5.tmpdir(), "cc-mirror-skill-"));
3326
+ try {
3327
+ let fetchResult = await cloneRepoAsync(tmpDir);
3328
+ if (!fetchResult.ok) {
3329
+ fetchResult = await downloadArchiveAsync(tmpDir);
3330
+ }
3331
+ if (!fetchResult.ok) {
3332
+ return { status: "failed", message: fetchResult.message || "skill fetch failed" };
3333
+ }
3334
+ const sourceDir = resolveSkillSourceDir(tmpDir);
3335
+ if (!sourceDir) {
3336
+ return { status: "failed", message: "skill source not found after download" };
3337
+ }
3338
+ if (exists) {
3339
+ fs10.rmSync(targetDir, { recursive: true, force: true });
3340
+ }
3341
+ copyDir(sourceDir, targetDir);
3342
+ fs10.writeFileSync(
3343
+ markerPath,
3344
+ JSON.stringify({ managedBy: "cc-mirror", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
3345
+ );
3346
+ return { status: exists ? "updated" : "installed", path: targetDir };
3347
+ } catch (error) {
3348
+ const message = error instanceof Error ? error.message : String(error);
3349
+ return { status: "failed", message };
3350
+ } finally {
3351
+ fs10.rmSync(tmpDir, { recursive: true, force: true });
3352
+ }
3353
+ };
3354
+
3355
+ // src/core/variant-builder/steps/SkillInstallStep.ts
3356
+ var SkillInstallStep = class {
3357
+ name = "SkillInstall";
3358
+ execute(ctx) {
3359
+ const { paths, prefs, state } = ctx;
3360
+ if (!prefs.skillInstallEnabled) {
3361
+ return;
3362
+ }
3363
+ ctx.report("Installing dev-browser skill...");
3364
+ const skillResult = ensureDevBrowserSkill({
3365
+ install: true,
3366
+ update: prefs.skillUpdateEnabled,
3367
+ targetDir: path13.join(paths.configDir, "skills")
3368
+ });
3369
+ if (skillResult.status === "failed") {
3370
+ state.notes.push(`dev-browser skill install failed: ${skillResult.message || "unknown error"}`);
3371
+ } else if (skillResult.status !== "skipped") {
3372
+ state.notes.push(`dev-browser skill ${skillResult.status}`);
3373
+ }
3374
+ }
3375
+ async executeAsync(ctx) {
3376
+ const { paths, prefs, state } = ctx;
3377
+ if (!prefs.skillInstallEnabled) {
3378
+ return;
3379
+ }
3380
+ await ctx.report("Installing dev-browser skill...");
3381
+ const skillResult = await ensureDevBrowserSkillAsync({
3382
+ install: true,
3383
+ update: prefs.skillUpdateEnabled,
3384
+ targetDir: path13.join(paths.configDir, "skills")
3385
+ });
3386
+ if (skillResult.status === "failed") {
3387
+ state.notes.push(`dev-browser skill install failed: ${skillResult.message || "unknown error"}`);
3388
+ } else if (skillResult.status !== "skipped") {
3389
+ state.notes.push(`dev-browser skill ${skillResult.status}`);
3390
+ }
3391
+ }
3392
+ };
3393
+
3394
+ // src/core/variant-builder/steps/FinalizeStep.ts
3395
+ import path14 from "node:path";
3396
+ var FinalizeStep = class {
3397
+ name = "Finalize";
3398
+ execute(ctx) {
3399
+ ctx.report("Finalizing variant...");
3400
+ this.finalize(ctx);
3401
+ }
3402
+ async executeAsync(ctx) {
3403
+ await ctx.report("Finalizing variant...");
3404
+ this.finalize(ctx);
3405
+ }
3406
+ finalize(ctx) {
3407
+ const { params, paths, prefs, state } = ctx;
3408
+ const meta = {
3409
+ name: params.name,
3410
+ provider: params.providerKey,
3411
+ baseUrl: params.baseUrl,
3412
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3413
+ claudeOrig: state.claudeBinary,
3414
+ binaryPath: state.binaryPath,
3415
+ configDir: paths.configDir,
3416
+ tweakDir: paths.tweakDir,
3417
+ brand: prefs.brandKey ?? void 0,
3418
+ promptPack: prefs.promptPackPreference,
3419
+ promptPackMode: prefs.promptPackModePreference,
3420
+ skillInstall: prefs.skillInstallEnabled,
3421
+ shellEnv: prefs.shellEnvEnabled,
3422
+ binDir: paths.resolvedBin,
3423
+ installType: "npm",
3424
+ npmDir: paths.npmDir,
3425
+ npmPackage: prefs.resolvedNpmPackage,
3426
+ npmVersion: prefs.resolvedNpmVersion
3427
+ };
3428
+ writeJson(path14.join(paths.variantDir, "variant.json"), meta);
3429
+ state.meta = meta;
3430
+ }
3431
+ };
3432
+
3433
+ // src/core/variant-builder/VariantBuilder.ts
3434
+ var normalizeNpmPackage = (value) => value && value.trim().length > 0 ? value.trim() : DEFAULT_NPM_PACKAGE;
3435
+ var normalizeNpmVersion = () => DEFAULT_NPM_VERSION;
3436
+ var shouldEnablePromptPack = (providerKey) => providerKey === "zai" || providerKey === "minimax";
3437
+ var defaultPromptPackMode = (providerKey) => providerKey === "zai" || providerKey === "minimax" ? "maximal" : "minimal";
3438
+ var shouldInstallSkills = (providerKey) => providerKey === "zai" || providerKey === "minimax";
3439
+ var shouldEnableShellEnv = (providerKey) => providerKey === "zai";
3440
+ var yieldToEventLoop = () => new Promise((resolve) => setImmediate(resolve));
3441
+ var VariantBuilder = class {
3442
+ constructor(isAsync = false) {
3443
+ this.isAsync = isAsync;
3444
+ this.steps = [
3445
+ new PrepareDirectoriesStep(),
3446
+ new InstallNpmStep(),
3447
+ new WriteConfigStep(),
3448
+ new BrandThemeStep(),
3449
+ new TweakccStep(),
3450
+ new WrapperStep(),
3451
+ new ShellEnvStep(),
3452
+ new SkillInstallStep(),
3453
+ new FinalizeStep()
3454
+ ];
3455
+ }
3456
+ steps;
3457
+ /**
3458
+ * Initialize the build context from params
3459
+ */
3460
+ initContext(params) {
3461
+ const provider = getProvider(params.providerKey);
3462
+ if (!provider) throw new Error(`Unknown provider: ${params.providerKey}`);
3463
+ if (!params.name) throw new Error("Variant name is required");
3464
+ const rootDir = params.rootDir ?? DEFAULT_ROOT;
3465
+ const binDir = params.binDir ?? DEFAULT_BIN_DIR;
3466
+ const resolvedRoot = expandTilde(rootDir) ?? rootDir;
3467
+ const resolvedBin = expandTilde(binDir) ?? binDir;
3468
+ const variantDir = path15.join(resolvedRoot, params.name);
3469
+ const configDir = path15.join(variantDir, "config");
3470
+ const tweakDir = path15.join(variantDir, "tweakcc");
3471
+ const wrapperPath = path15.join(resolvedBin, params.name);
3472
+ const npmDir = path15.join(variantDir, "npm");
3473
+ const paths = {
3474
+ resolvedRoot,
3475
+ resolvedBin,
3476
+ variantDir,
3477
+ configDir,
3478
+ tweakDir,
3479
+ wrapperPath,
3480
+ npmDir
3481
+ };
3482
+ const resolvedNpmPackage = normalizeNpmPackage(params.npmPackage);
3483
+ const resolvedNpmVersion = normalizeNpmVersion();
3484
+ const promptPackPreference = params.promptPack ?? shouldEnablePromptPack(params.providerKey);
3485
+ const promptPackModePreference = params.promptPackMode ?? defaultPromptPackMode(params.providerKey);
3486
+ const promptPackEnabled = !params.noTweak && promptPackPreference;
3487
+ const skillInstallEnabled = params.skillInstall ?? shouldInstallSkills(params.providerKey);
3488
+ const shellEnvEnabled = params.shellEnv ?? shouldEnableShellEnv(params.providerKey);
3489
+ const skillUpdateEnabled = Boolean(params.skillUpdate);
3490
+ const commandStdio = params.tweakccStdio ?? "inherit";
3491
+ const prefs = {
3492
+ resolvedNpmPackage,
3493
+ resolvedNpmVersion,
3494
+ promptPackPreference,
3495
+ promptPackModePreference,
3496
+ promptPackEnabled,
3497
+ skillInstallEnabled,
3498
+ shellEnvEnabled,
3499
+ skillUpdateEnabled,
3500
+ brandKey: null,
3501
+ // Will be resolved in BrandThemeStep
3502
+ commandStdio
3503
+ };
3504
+ const state = {
3505
+ binaryPath: "",
3506
+ claudeBinary: "",
3507
+ notes: [],
3508
+ tweakResult: null
3509
+ };
3510
+ const report = this.isAsync ? async (step) => {
3511
+ params.onProgress?.(step);
3512
+ await yieldToEventLoop();
3513
+ } : (step) => {
3514
+ params.onProgress?.(step);
3515
+ };
3516
+ return {
3517
+ params,
3518
+ provider,
3519
+ paths,
3520
+ prefs,
3521
+ state,
3522
+ report,
3523
+ isAsync: this.isAsync
3524
+ };
3525
+ }
3526
+ /**
3527
+ * Build a variant synchronously
3528
+ */
3529
+ build(params) {
3530
+ if (this.isAsync) {
3531
+ throw new Error("Use buildAsync() for async builds");
3532
+ }
3533
+ const ctx = this.initContext(params);
3534
+ for (const step of this.steps) {
3535
+ step.execute(ctx);
3536
+ }
3537
+ return this.toResult(ctx);
3538
+ }
3539
+ /**
3540
+ * Build a variant asynchronously
3541
+ */
3542
+ async buildAsync(params) {
3543
+ if (!this.isAsync) {
3544
+ throw new Error("Use build() for sync builds");
3545
+ }
3546
+ const ctx = this.initContext(params);
3547
+ for (const step of this.steps) {
3548
+ await step.executeAsync(ctx);
3549
+ }
3550
+ return this.toResult(ctx);
3551
+ }
3552
+ /**
3553
+ * Convert build context to result
3554
+ */
3555
+ toResult(ctx) {
3556
+ if (!ctx.state.meta) {
3557
+ throw new Error("FinalizeStep did not populate meta");
3558
+ }
3559
+ return {
3560
+ meta: ctx.state.meta,
3561
+ wrapperPath: ctx.paths.wrapperPath,
3562
+ tweakResult: ctx.state.tweakResult,
3563
+ notes: ctx.state.notes.length > 0 ? ctx.state.notes : void 0
3564
+ };
3565
+ }
3566
+ };
3567
+
3568
+ // src/core/variant-builder/VariantUpdater.ts
3569
+ import path19 from "node:path";
3570
+
3571
+ // src/core/variant-builder/update-steps/InstallNpmUpdateStep.ts
3572
+ var InstallNpmUpdateStep = class {
3573
+ name = "InstallNpm";
3574
+ execute(ctx) {
3575
+ if (ctx.opts.settingsOnly) return;
3576
+ ctx.report(`Installing ${ctx.prefs.resolvedNpmPackage}@${ctx.prefs.resolvedNpmVersion}...`);
3577
+ this.install(ctx, false);
3578
+ }
3579
+ async executeAsync(ctx) {
3580
+ if (ctx.opts.settingsOnly) return;
3581
+ await ctx.report(`Installing ${ctx.prefs.resolvedNpmPackage}@${ctx.prefs.resolvedNpmVersion}...`);
3582
+ await this.install(ctx, true);
3583
+ }
3584
+ async install(ctx, isAsync) {
3585
+ const { meta, paths, prefs } = ctx;
3586
+ ensureDir(paths.npmDir);
3587
+ const installOpts = {
3588
+ npmDir: paths.npmDir,
3589
+ npmPackage: prefs.resolvedNpmPackage,
3590
+ npmVersion: prefs.resolvedNpmVersion,
3591
+ stdio: prefs.commandStdio
3592
+ };
3593
+ const install = isAsync ? await installNpmClaudeAsync(installOpts) : installNpmClaude(installOpts);
3594
+ meta.binaryPath = install.cliPath;
3595
+ meta.installType = "npm";
3596
+ meta.npmDir = paths.npmDir;
3597
+ meta.npmPackage = prefs.resolvedNpmPackage;
3598
+ meta.npmVersion = prefs.resolvedNpmVersion;
3599
+ meta.claudeOrig = `npm:${prefs.resolvedNpmPackage}@${prefs.resolvedNpmVersion}`;
3600
+ }
3601
+ };
3602
+
3603
+ // src/core/variant-builder/update-steps/ModelOverridesStep.ts
3604
+ var ModelOverridesStep = class {
3605
+ name = "ModelOverrides";
3606
+ execute(ctx) {
3607
+ this.apply(ctx);
3608
+ }
3609
+ async executeAsync(ctx) {
3610
+ this.apply(ctx);
3611
+ }
3612
+ apply(ctx) {
3613
+ const { opts, meta, state } = ctx;
3614
+ if (!opts.modelOverrides || Object.keys(opts.modelOverrides).length === 0) {
3615
+ return;
3616
+ }
3617
+ const envOverridesUpdated = ensureSettingsEnvOverrides(meta.configDir, {
3618
+ ...opts.modelOverrides.sonnet ? { ANTHROPIC_DEFAULT_SONNET_MODEL: opts.modelOverrides.sonnet } : {},
3619
+ ...opts.modelOverrides.opus ? { ANTHROPIC_DEFAULT_OPUS_MODEL: opts.modelOverrides.opus } : {},
3620
+ ...opts.modelOverrides.haiku ? { ANTHROPIC_DEFAULT_HAIKU_MODEL: opts.modelOverrides.haiku } : {},
3621
+ ...opts.modelOverrides.smallFast ? { ANTHROPIC_SMALL_FAST_MODEL: opts.modelOverrides.smallFast } : {},
3622
+ ...opts.modelOverrides.defaultModel ? { ANTHROPIC_MODEL: opts.modelOverrides.defaultModel } : {},
3623
+ ...opts.modelOverrides.subagentModel ? { CLAUDE_CODE_SUBAGENT_MODEL: opts.modelOverrides.subagentModel } : {}
3624
+ });
3625
+ if (envOverridesUpdated) {
3626
+ state.notes.push("Updated model mapping in settings.json.");
3627
+ }
3628
+ }
3629
+ };
3630
+
3631
+ // src/core/variant-builder/update-steps/TweakccUpdateStep.ts
3632
+ var TweakccUpdateStep = class {
3633
+ name = "Tweakcc";
3634
+ execute(ctx) {
3635
+ if (ctx.opts.noTweak) return;
3636
+ ctx.report("Running tweakcc patches...");
3637
+ this.runTweakcc(ctx, false);
3638
+ }
3639
+ async executeAsync(ctx) {
3640
+ if (ctx.opts.noTweak) return;
3641
+ await ctx.report("Running tweakcc patches...");
3642
+ await this.runTweakcc(ctx, true);
3643
+ }
3644
+ async runTweakcc(ctx, isAsync) {
3645
+ const { opts, meta, prefs, state } = ctx;
3646
+ ensureDir(meta.tweakDir);
3647
+ if (opts.brand !== void 0) {
3648
+ state.brandKey = resolveBrandKey(meta.provider, opts.brand);
3649
+ meta.brand = state.brandKey ?? void 0;
3650
+ }
3651
+ ensureTweakccConfig(meta.tweakDir, state.brandKey);
3652
+ const tweakResult = isAsync ? await runTweakccAsync(meta.tweakDir, meta.binaryPath, prefs.commandStdio) : runTweakcc(meta.tweakDir, meta.binaryPath, prefs.commandStdio);
3653
+ state.tweakResult = tweakResult;
3654
+ if (tweakResult.status !== 0) {
3655
+ const output = `${tweakResult.stderr ?? ""}
3656
+ ${tweakResult.stdout ?? ""}`.trim();
3657
+ throw new Error(formatTweakccFailure(output));
3658
+ }
3659
+ if (prefs.promptPackEnabled) {
3660
+ if (isAsync) {
3661
+ await ctx.report(`Applying prompt pack (${prefs.promptPackModePreference})...`);
3662
+ } else {
3663
+ ctx.report(`Applying prompt pack (${prefs.promptPackModePreference})...`);
3664
+ }
3665
+ const packResult = applyPromptPack(meta.tweakDir, meta.provider, prefs.promptPackModePreference);
3666
+ if (packResult.changed) {
3667
+ state.notes.push(`Prompt pack applied (${packResult.mode}, ${packResult.updated.join(", ")})`);
3668
+ if (isAsync) {
3669
+ await ctx.report("Re-applying tweakcc...");
3670
+ } else {
3671
+ ctx.report("Re-applying tweakcc...");
3672
+ }
3673
+ const reapply = isAsync ? await runTweakccAsync(meta.tweakDir, meta.binaryPath, prefs.commandStdio) : runTweakcc(meta.tweakDir, meta.binaryPath, prefs.commandStdio);
3674
+ state.tweakResult = reapply;
3675
+ if (reapply.status !== 0) {
3676
+ const output = `${reapply.stderr ?? ""}
3677
+ ${reapply.stdout ?? ""}`.trim();
3678
+ throw new Error(formatTweakccFailure(output));
3679
+ }
3680
+ }
3681
+ }
3682
+ }
3683
+ };
3684
+
3685
+ // src/core/variant-builder/update-steps/WrapperUpdateStep.ts
3686
+ import path16 from "node:path";
3687
+ var WrapperUpdateStep = class {
3688
+ name = "Wrapper";
3689
+ execute(ctx) {
3690
+ if (ctx.opts.settingsOnly) return;
3691
+ ctx.report("Writing CLI wrapper...");
3692
+ this.writeWrapper(ctx);
3693
+ }
3694
+ async executeAsync(ctx) {
3695
+ if (ctx.opts.settingsOnly) return;
3696
+ await ctx.report("Writing CLI wrapper...");
3697
+ this.writeWrapper(ctx);
3698
+ }
3699
+ writeWrapper(ctx) {
3700
+ const { name, opts, meta } = ctx;
3701
+ const resolvedBin = opts.binDir ? expandTilde(opts.binDir) ?? opts.binDir : meta.binDir;
3702
+ if (resolvedBin) {
3703
+ ensureDir(resolvedBin);
3704
+ const wrapperPath = path16.join(resolvedBin, name);
3705
+ writeWrapper(wrapperPath, meta.configDir, meta.binaryPath, "node");
3706
+ meta.binDir = resolvedBin;
3707
+ }
3708
+ }
3709
+ };
3710
+
3711
+ // src/core/variant-builder/update-steps/ConfigUpdateStep.ts
3712
+ var ConfigUpdateStep = class {
3713
+ name = "Config";
3714
+ execute(ctx) {
3715
+ ctx.report("Updating configuration...");
3716
+ this.updateConfig(ctx, false);
3717
+ }
3718
+ async executeAsync(ctx) {
3719
+ await ctx.report("Updating configuration...");
3720
+ await this.updateConfig(ctx, true);
3721
+ }
3722
+ async updateConfig(ctx, isAsync) {
3723
+ const { opts, meta, state } = ctx;
3724
+ ensureApiKeyApproval(meta.configDir);
3725
+ if (meta.provider === "minimax") {
3726
+ if (isAsync) {
3727
+ await ctx.report("Configuring MiniMax MCP server...");
3728
+ } else {
3729
+ ctx.report("Configuring MiniMax MCP server...");
3730
+ }
3731
+ ensureMinimaxMcpServer(meta.configDir);
3732
+ }
3733
+ if (meta.provider === "zai") {
3734
+ const denied = ensureZaiMcpDeny(meta.configDir);
3735
+ if (denied) {
3736
+ state.notes.push("Blocked Z.ai-injected MCP tools in settings.json.");
3737
+ }
3738
+ }
3739
+ const brandThemeId = !opts.noTweak && state.brandKey ? getBrandThemeId(state.brandKey) : null;
3740
+ const onboarding = ensureOnboardingState(meta.configDir, {
3741
+ themeId: brandThemeId ?? "dark",
3742
+ forceTheme: Boolean(brandThemeId)
3743
+ });
3744
+ const envDefaultsUpdated = ensureSettingsEnvDefaults(meta.configDir, {
3745
+ TWEAKCC_CONFIG_DIR: meta.tweakDir,
3746
+ DISABLE_AUTOUPDATER: "1",
3747
+ CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: "1"
3748
+ });
3749
+ if (envDefaultsUpdated) {
3750
+ state.notes.push("Disabled Claude Code auto-updater (DISABLE_AUTOUPDATER=1).");
3751
+ }
3752
+ if (onboarding.themeChanged) {
3753
+ state.notes.push(`Default theme set to ${brandThemeId ?? "dark"}.`);
3754
+ }
3755
+ if (onboarding.onboardingChanged) {
3756
+ state.notes.push("Onboarding marked complete.");
3757
+ }
3758
+ }
3759
+ };
3760
+
3761
+ // src/core/variant-builder/update-steps/ShellEnvUpdateStep.ts
3762
+ var ShellEnvUpdateStep = class {
3763
+ name = "ShellEnv";
3764
+ execute(ctx) {
3765
+ if (ctx.opts.settingsOnly) return;
3766
+ this.configure(ctx, false);
3767
+ }
3768
+ async executeAsync(ctx) {
3769
+ if (ctx.opts.settingsOnly) return;
3770
+ await this.configure(ctx, true);
3771
+ }
3772
+ async configure(ctx, isAsync) {
3773
+ const { opts, meta, prefs, state } = ctx;
3774
+ if (prefs.shellEnvEnabled && meta.provider === "zai") {
3775
+ if (isAsync) {
3776
+ await ctx.report("Configuring shell environment...");
3777
+ } else {
3778
+ ctx.report("Configuring shell environment...");
3779
+ }
3780
+ const shellResult = ensureZaiShellEnv({ configDir: meta.configDir });
3781
+ if (shellResult.status === "updated") {
3782
+ const suffix = shellResult.message ? ` (${shellResult.message})` : "";
3783
+ state.notes.push(`Z_AI_API_KEY written to ${shellResult.path}${suffix}`);
3784
+ } else if (shellResult.status === "failed") {
3785
+ state.notes.push(`Z_AI_API_KEY not written: ${shellResult.message || "unknown error"}`);
3786
+ } else if (shellResult.message) {
3787
+ state.notes.push(`Z_AI_API_KEY: ${shellResult.message}`);
3788
+ }
3789
+ } else if (meta.provider === "zai" && opts.shellEnv === false) {
3790
+ state.notes.push("Z_AI_API_KEY not written to shell profile. Set it manually in your shell rc file.");
3791
+ }
3792
+ }
3793
+ };
3794
+
3795
+ // src/core/variant-builder/update-steps/SkillInstallUpdateStep.ts
3796
+ import path17 from "node:path";
3797
+ var SkillInstallUpdateStep = class {
3798
+ name = "SkillInstall";
3799
+ execute(ctx) {
3800
+ if (ctx.opts.settingsOnly) return;
3801
+ if (!ctx.prefs.skillInstallEnabled) return;
3802
+ ctx.report("Installing dev-browser skill...");
3803
+ this.install(ctx, false);
3804
+ }
3805
+ async executeAsync(ctx) {
3806
+ if (ctx.opts.settingsOnly) return;
3807
+ if (!ctx.prefs.skillInstallEnabled) return;
3808
+ await ctx.report("Installing dev-browser skill...");
3809
+ await this.install(ctx, true);
3810
+ }
3811
+ async install(ctx, isAsync) {
3812
+ const { meta, prefs, state } = ctx;
3813
+ const skillOpts = {
3814
+ install: true,
3815
+ update: prefs.skillUpdateEnabled,
3816
+ targetDir: path17.join(meta.configDir, "skills")
3817
+ };
3818
+ const skillResult = isAsync ? await ensureDevBrowserSkillAsync(skillOpts) : ensureDevBrowserSkill(skillOpts);
3819
+ if (skillResult.status === "failed") {
3820
+ state.notes.push(`dev-browser skill install failed: ${skillResult.message || "unknown error"}`);
3821
+ } else if (skillResult.status !== "skipped") {
3822
+ state.notes.push(`dev-browser skill ${skillResult.status}`);
3823
+ }
3824
+ }
3825
+ };
3826
+
3827
+ // src/core/variant-builder/update-steps/FinalizeUpdateStep.ts
3828
+ import path18 from "node:path";
3829
+ var FinalizeUpdateStep = class {
3830
+ name = "Finalize";
3831
+ execute(ctx) {
3832
+ ctx.report("Finalizing variant...");
3833
+ this.finalize(ctx);
3834
+ }
3835
+ async executeAsync(ctx) {
3836
+ await ctx.report("Finalizing variant...");
3837
+ this.finalize(ctx);
3838
+ }
3839
+ finalize(ctx) {
3840
+ const { meta, paths, prefs } = ctx;
3841
+ meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3842
+ meta.promptPack = prefs.promptPackPreference;
3843
+ meta.promptPackMode = prefs.promptPackModePreference;
3844
+ meta.skillInstall = prefs.skillInstallEnabled;
3845
+ meta.shellEnv = prefs.shellEnvEnabled;
3846
+ writeJson(path18.join(paths.variantDir, "variant.json"), meta);
3847
+ }
3848
+ };
3849
+
3850
+ // src/core/variant-builder/VariantUpdater.ts
3851
+ var normalizeNpmPackage2 = (value) => value && value.trim().length > 0 ? value.trim() : DEFAULT_NPM_PACKAGE;
3852
+ var normalizeNpmVersion2 = () => DEFAULT_NPM_VERSION;
3853
+ var shouldEnablePromptPack2 = (providerKey) => providerKey === "zai" || providerKey === "minimax";
3854
+ var defaultPromptPackMode2 = (providerKey) => providerKey === "zai" || providerKey === "minimax" ? "maximal" : "minimal";
3855
+ var shouldInstallSkills2 = (providerKey) => providerKey === "zai" || providerKey === "minimax";
3856
+ var shouldEnableShellEnv2 = (providerKey) => providerKey === "zai";
3857
+ var yieldToEventLoop2 = () => new Promise((resolve) => setImmediate(resolve));
3858
+ var VariantUpdater = class {
3859
+ constructor(isAsync = false) {
3860
+ this.isAsync = isAsync;
3861
+ this.steps = [
3862
+ new InstallNpmUpdateStep(),
3863
+ new ModelOverridesStep(),
3864
+ new TweakccUpdateStep(),
3865
+ new WrapperUpdateStep(),
3866
+ new ConfigUpdateStep(),
3867
+ new ShellEnvUpdateStep(),
3868
+ new SkillInstallUpdateStep(),
3869
+ new FinalizeUpdateStep()
3870
+ ];
3871
+ }
3872
+ steps;
3873
+ /**
3874
+ * Initialize the update context
3875
+ */
3876
+ initContext(rootDir, name, opts) {
3877
+ const resolvedRoot = expandTilde(rootDir || DEFAULT_ROOT) ?? rootDir;
3878
+ const variantDir = path19.join(resolvedRoot, name);
3879
+ const meta = loadVariantMeta(variantDir);
3880
+ if (!meta) throw new Error(`Variant not found: ${name}`);
3881
+ const resolvedNpmPackage = normalizeNpmPackage2(opts.npmPackage ?? meta.npmPackage);
3882
+ const resolvedNpmVersion = normalizeNpmVersion2();
3883
+ const promptPackPreference = opts.promptPack ?? meta.promptPack ?? shouldEnablePromptPack2(meta.provider);
3884
+ const promptPackModePreference = opts.promptPackMode ?? meta.promptPackMode ?? defaultPromptPackMode2(meta.provider);
3885
+ const promptPackEnabled = !opts.noTweak && promptPackPreference;
3886
+ const skillInstallEnabled = opts.skillInstall ?? meta.skillInstall ?? shouldInstallSkills2(meta.provider);
3887
+ const shellEnvEnabled = opts.shellEnv ?? meta.shellEnv ?? shouldEnableShellEnv2(meta.provider);
3888
+ const skillUpdateEnabled = Boolean(opts.skillUpdate);
3889
+ const commandStdio = opts.tweakccStdio || "inherit";
3890
+ const paths = {
3891
+ resolvedRoot,
3892
+ resolvedBin: opts.binDir ? expandTilde(opts.binDir) ?? opts.binDir : meta.binDir,
3893
+ variantDir,
3894
+ npmDir: meta.npmDir || path19.join(variantDir, "npm")
3895
+ };
3896
+ const prefs = {
3897
+ resolvedNpmPackage,
3898
+ resolvedNpmVersion,
3899
+ promptPackPreference,
3900
+ promptPackModePreference,
3901
+ promptPackEnabled,
3902
+ skillInstallEnabled,
3903
+ shellEnvEnabled,
3904
+ skillUpdateEnabled,
3905
+ commandStdio
3906
+ };
3907
+ const state = {
3908
+ notes: [],
3909
+ tweakResult: null,
3910
+ brandKey: meta.brand ?? null
3911
+ };
3912
+ const report = this.isAsync ? async (step) => {
3913
+ opts.onProgress?.(step);
3914
+ await yieldToEventLoop2();
3915
+ } : (step) => {
3916
+ opts.onProgress?.(step);
3917
+ };
3918
+ return {
3919
+ name,
3920
+ opts,
3921
+ meta,
3922
+ paths,
3923
+ prefs,
3924
+ state,
3925
+ report,
3926
+ isAsync: this.isAsync
3927
+ };
3928
+ }
3929
+ /**
3930
+ * Update a variant synchronously
3931
+ */
3932
+ update(rootDir, name, opts = {}) {
3933
+ if (this.isAsync) {
3934
+ throw new Error("Use updateAsync() for async updates");
3935
+ }
3936
+ const ctx = this.initContext(rootDir, name, opts);
3937
+ for (const step of this.steps) {
3938
+ step.execute(ctx);
3939
+ }
3940
+ return this.toResult(ctx);
3941
+ }
3942
+ /**
3943
+ * Update a variant asynchronously
3944
+ */
3945
+ async updateAsync(rootDir, name, opts = {}) {
3946
+ if (!this.isAsync) {
3947
+ throw new Error("Use update() for sync updates");
3948
+ }
3949
+ const ctx = this.initContext(rootDir, name, opts);
3950
+ for (const step of this.steps) {
3951
+ await step.executeAsync(ctx);
3952
+ }
3953
+ return this.toResult(ctx);
3954
+ }
3955
+ /**
3956
+ * Convert update context to result
3957
+ */
3958
+ toResult(ctx) {
3959
+ return {
3960
+ meta: ctx.meta,
3961
+ tweakResult: ctx.state.tweakResult,
3962
+ notes: ctx.state.notes.length > 0 ? ctx.state.notes : void 0
3963
+ };
3964
+ }
3965
+ };
3966
+
3967
+ // src/core/index.ts
3968
+ var createVariant = (params) => {
3969
+ return new VariantBuilder(false).build(params);
3970
+ };
3971
+ var updateVariant = (rootDir, name, opts = {}) => {
3972
+ return new VariantUpdater(false).update(rootDir, name, opts);
3973
+ };
3974
+ var removeVariant = (rootDir, name) => {
3975
+ const resolvedRoot = expandTilde(rootDir || DEFAULT_ROOT) ?? rootDir;
3976
+ const variantDir = path20.join(resolvedRoot, name);
3977
+ if (!fs11.existsSync(variantDir)) throw new Error(`Variant not found: ${name}`);
3978
+ fs11.rmSync(variantDir, { recursive: true, force: true });
3979
+ };
3980
+ var doctor = (rootDir, binDir) => {
3981
+ const resolvedRoot = expandTilde(rootDir || DEFAULT_ROOT) ?? rootDir;
3982
+ const resolvedBin = expandTilde(binDir || DEFAULT_BIN_DIR) ?? binDir;
3983
+ const variants = listVariants(resolvedRoot);
3984
+ return variants.map(({ name, meta }) => {
3985
+ const wrapperPath = path20.join(resolvedBin, name);
3986
+ const ok = Boolean(meta && fs11.existsSync(meta.binaryPath) && fs11.existsSync(wrapperPath));
3987
+ return {
3988
+ name,
3989
+ ok,
3990
+ binaryPath: meta?.binaryPath,
3991
+ wrapperPath
3992
+ };
3993
+ });
3994
+ };
3995
+ var listVariants2 = (rootDir) => listVariants(rootDir);
3996
+ var tweakVariant = (rootDir, name) => {
3997
+ const resolvedRoot = expandTilde(rootDir || DEFAULT_ROOT) ?? rootDir;
3998
+ const variantDir = path20.join(resolvedRoot, name);
3999
+ const meta = loadVariantMeta(variantDir);
4000
+ if (!meta) throw new Error(`Variant not found: ${name}`);
4001
+ ensureDir(meta.tweakDir);
4002
+ const brandKey = meta.brand ?? null;
4003
+ ensureTweakccConfig(meta.tweakDir, brandKey);
4004
+ const result = launchTweakccUi(meta.tweakDir, meta.binaryPath);
4005
+ if (result.status && result.status !== 0) {
4006
+ const output = `${result.stderr ?? ""}
4007
+ ${result.stdout ?? ""}`.trim();
4008
+ throw new Error(formatTweakccFailure(output));
4009
+ }
4010
+ };
4011
+
4012
+ // src/cli/commands/list.ts
4013
+ function runListCommand({ opts }) {
4014
+ const rootDir = opts.root || DEFAULT_ROOT;
4015
+ const variants = listVariants2(rootDir);
4016
+ if (variants.length === 0) {
4017
+ console.log(`No variants found in ${rootDir}`);
4018
+ return;
4019
+ }
4020
+ for (const entry of variants) {
4021
+ console.log(entry.name);
4022
+ }
4023
+ }
4024
+
4025
+ // src/cli/doctor.ts
4026
+ var printDoctor = (report) => {
4027
+ if (report.length === 0) {
4028
+ console.log("No variants found.");
4029
+ return;
4030
+ }
4031
+ for (const item of report) {
4032
+ console.log(`${item.ok ? "\u2713" : "\u2717"} ${item.name}`);
4033
+ if (!item.ok) {
4034
+ console.log(` binary: ${item.binaryPath ?? "missing"}`);
4035
+ console.log(` wrapper: ${item.wrapperPath}`);
4036
+ }
4037
+ }
4038
+ };
4039
+
4040
+ // src/cli/commands/doctorCmd.ts
4041
+ function runDoctorCommand({ opts }) {
4042
+ const rootDir = opts.root || DEFAULT_ROOT;
4043
+ const binDir = opts["bin-dir"] || DEFAULT_BIN_DIR;
4044
+ const report = doctor(rootDir, binDir);
4045
+ printDoctor(report);
4046
+ }
4047
+
4048
+ // src/cli/commands/remove.ts
4049
+ function runRemoveCommand({ opts }) {
4050
+ const target = opts._ && opts._[0];
4051
+ if (!target) {
4052
+ console.error("remove requires a variant name");
4053
+ process.exit(1);
4054
+ }
4055
+ const rootDir = opts.root || DEFAULT_ROOT;
4056
+ removeVariant(rootDir, target);
4057
+ console.log(`Removed ${target}`);
4058
+ }
4059
+
4060
+ // src/cli/commands/tweak.ts
4061
+ function runTweakCommand({ opts }) {
4062
+ const target = opts._ && opts._[0];
4063
+ if (!target) {
4064
+ console.error("tweak requires a variant name");
4065
+ process.exit(1);
4066
+ }
4067
+ const rootDir = opts.root || DEFAULT_ROOT;
4068
+ tweakVariant(rootDir, target);
4069
+ }
4070
+
4071
+ // src/cli/commands/update.ts
4072
+ import path21 from "node:path";
4073
+
4074
+ // src/cli/utils/buildShareUrl.ts
4075
+ function buildShareUrl(providerLabel, variant, mode) {
4076
+ const lines = [
4077
+ `Just set up ${providerLabel} with cc-mirror`,
4078
+ mode ? `Prompt pack: ${mode}` : "Prompt pack: enabled",
4079
+ `CLI: ${variant}`,
4080
+ "Get yours: npx cc-mirror",
4081
+ "(Attach your TUI screenshot)"
4082
+ ];
4083
+ const url = new URL("https://x.com/intent/tweet");
4084
+ url.searchParams.set("text", lines.join("\n"));
4085
+ return url.toString();
4086
+ }
4087
+
4088
+ // src/cli/utils/printSummary.ts
4089
+ function printSummary(opts) {
4090
+ const { action, meta, wrapperPath, notes, shareUrl } = opts;
4091
+ console.log(`
4092
+ ${action}: ${meta.name}`);
4093
+ console.log(`Provider: ${meta.provider}`);
4094
+ if (meta.promptPack !== void 0) {
4095
+ const mode = meta.promptPackMode || "maximal";
4096
+ console.log(`Prompt pack: ${meta.promptPack ? `on (${mode})` : "off"}`);
4097
+ }
4098
+ if (meta.skillInstall !== void 0) {
4099
+ console.log(`dev-browser skill: ${meta.skillInstall ? "on" : "off"}`);
4100
+ }
4101
+ if (meta.shellEnv !== void 0 && meta.provider === "zai") {
4102
+ console.log(`Shell env: ${meta.shellEnv ? "write Z_AI_API_KEY" : "manual"}`);
4103
+ }
4104
+ if (wrapperPath) console.log(`Wrapper: ${wrapperPath}`);
4105
+ if (meta.configDir) console.log(`Config: ${meta.configDir}`);
4106
+ if (notes && notes.length > 0) {
4107
+ console.log("Notes:");
4108
+ for (const note of notes) console.log(`- ${note}`);
4109
+ }
4110
+ console.log("Next steps:");
4111
+ console.log(`- Run: ${meta.name}`);
4112
+ console.log(`- Update: cc-mirror update ${meta.name}`);
4113
+ console.log(`- Tweak: cc-mirror tweak ${meta.name}`);
4114
+ console.log("Help: cc-mirror help");
4115
+ if (shareUrl) {
4116
+ console.log("Share:");
4117
+ console.log(shareUrl);
4118
+ }
4119
+ console.log("");
4120
+ }
4121
+
4122
+ // src/cli/prompt.ts
4123
+ import readline from "node:readline";
4124
+ var prompt = async (question, defaultValue) => {
4125
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4126
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
4127
+ return new Promise((resolve) => {
4128
+ rl.question(`${question}${suffix}: `, (answer) => {
4129
+ rl.close();
4130
+ const trimmed = answer.trim();
4131
+ resolve(trimmed.length > 0 ? trimmed : defaultValue ?? "");
4132
+ });
4133
+ });
4134
+ };
4135
+
4136
+ // src/cli/utils/requirePrompt.ts
4137
+ async function requirePrompt(label, value) {
4138
+ let next = (value ?? "").trim();
4139
+ while (!next) {
4140
+ next = (await prompt(label, value)).trim();
4141
+ if (!next) {
4142
+ console.log("Value required.");
4143
+ }
4144
+ }
4145
+ return next;
4146
+ }
4147
+
4148
+ // src/cli/utils/modelOverrides.ts
4149
+ function getModelOverridesFromArgs(opts) {
4150
+ return {
4151
+ sonnet: typeof opts["model-sonnet"] === "string" ? opts["model-sonnet"] : void 0,
4152
+ opus: typeof opts["model-opus"] === "string" ? opts["model-opus"] : void 0,
4153
+ haiku: typeof opts["model-haiku"] === "string" ? opts["model-haiku"] : void 0,
4154
+ smallFast: typeof opts["model-small-fast"] === "string" ? opts["model-small-fast"] : void 0,
4155
+ defaultModel: typeof opts["model-default"] === "string" ? opts["model-default"] : void 0,
4156
+ subagentModel: typeof opts["model-subagent"] === "string" ? opts["model-subagent"] : void 0
4157
+ };
4158
+ }
4159
+ async function ensureModelMapping(providerKey, opts, overrides) {
4160
+ const provider = getProvider(providerKey);
4161
+ if (!provider?.requiresModelMapping) return overrides;
4162
+ const missing = {
4163
+ sonnet: (overrides.sonnet ?? "").trim().length === 0,
4164
+ opus: (overrides.opus ?? "").trim().length === 0,
4165
+ haiku: (overrides.haiku ?? "").trim().length === 0
4166
+ };
4167
+ if (opts.yes && (missing.sonnet || missing.opus || missing.haiku)) {
4168
+ throw new Error("OpenRouter/Local LLMs require --model-sonnet/--model-opus/--model-haiku");
4169
+ }
4170
+ if (!opts.yes) {
4171
+ if (missing.sonnet) overrides.sonnet = await requirePrompt("Default Sonnet model", overrides.sonnet);
4172
+ if (missing.opus) overrides.opus = await requirePrompt("Default Opus model", overrides.opus);
4173
+ if (missing.haiku) overrides.haiku = await requirePrompt("Default Haiku model", overrides.haiku);
4174
+ }
4175
+ return overrides;
4176
+ }
4177
+ function formatModelNote(overrides) {
4178
+ const entries = [
4179
+ ["sonnet", overrides.sonnet],
4180
+ ["opus", overrides.opus],
4181
+ ["haiku", overrides.haiku]
4182
+ ].filter(([, value]) => value && String(value).trim().length > 0);
4183
+ if (entries.length === 0) return null;
4184
+ const text = entries.map(([key, value]) => `${key}=${value}`).join(", ");
4185
+ return `Model mapping: ${text}`;
4186
+ }
4187
+
4188
+ // src/cli/utils/promptPack.ts
4189
+ function parsePromptPackMode(value) {
4190
+ if (!value) return void 0;
4191
+ const normalized = value.toLowerCase();
4192
+ if (normalized === "minimal" || normalized === "maximal") return normalized;
4193
+ return void 0;
4194
+ }
4195
+
4196
+ // src/cli/utils/extraEnv.ts
4197
+ function buildExtraEnv(opts) {
4198
+ const env = Array.isArray(opts.env) ? [...opts.env] : [];
4199
+ const timeout = opts["timeout-ms"];
4200
+ if (typeof timeout === "string" && timeout.trim().length > 0) {
4201
+ env.push(`API_TIMEOUT_MS=${timeout}`);
4202
+ }
4203
+ return env;
4204
+ }
4205
+
4206
+ // src/cli/commands/update.ts
4207
+ function runUpdateCommand({ opts }) {
4208
+ const target = opts._ && opts._[0];
4209
+ const rootDir = opts.root || DEFAULT_ROOT;
4210
+ const binDir = opts["bin-dir"] || DEFAULT_BIN_DIR;
4211
+ const names = target ? [target] : listVariants2(rootDir).map((entry) => entry.name);
4212
+ if (names.length === 0) {
4213
+ console.log(`No variants found in ${rootDir}`);
4214
+ return;
4215
+ }
4216
+ const promptPack = opts["no-prompt-pack"] ? false : void 0;
4217
+ const promptPackMode = parsePromptPackMode(opts["prompt-pack-mode"]);
4218
+ const skillInstall = opts["no-skill-install"] ? false : void 0;
4219
+ const skillUpdate = Boolean(opts["skill-update"]);
4220
+ const shellEnv = opts["no-shell-env"] ? false : opts["shell-env"] ? true : void 0;
4221
+ for (const name of names) {
4222
+ const result = updateVariant(rootDir, name, {
4223
+ binDir,
4224
+ npmPackage: opts["npm-package"],
4225
+ brand: opts.brand,
4226
+ noTweak: Boolean(opts.noTweak),
4227
+ promptPack,
4228
+ promptPackMode,
4229
+ skillInstall,
4230
+ shellEnv,
4231
+ skillUpdate
4232
+ });
4233
+ const wrapperPath = path21.join(binDir, name);
4234
+ printSummary({
4235
+ action: "Updated",
4236
+ meta: result.meta,
4237
+ wrapperPath,
4238
+ notes: result.notes
4239
+ });
4240
+ }
4241
+ }
4242
+
4243
+ // src/cli/commands/create.ts
4244
+ async function prepareCreateParams(opts) {
4245
+ let providerKey = opts.provider;
4246
+ if (!providerKey && !opts.yes) {
4247
+ const providers = listProviders().map((p) => p.key).join(", ");
4248
+ providerKey = await prompt(`Provider (${providers})`, "zai");
4249
+ }
4250
+ providerKey = providerKey || "zai";
4251
+ const provider = getProvider(providerKey);
4252
+ if (!provider) {
4253
+ throw new Error(`Unknown provider: ${providerKey}`);
4254
+ }
4255
+ const name = opts.name || providerKey;
4256
+ const baseUrl = opts["base-url"] || provider.baseUrl;
4257
+ const envZaiKey = providerKey === "zai" ? process.env.Z_AI_API_KEY : void 0;
4258
+ const envAnthropicKey = providerKey === "zai" ? process.env.ANTHROPIC_API_KEY : void 0;
4259
+ const hasApiKeyFlag = Boolean(opts["api-key"]);
4260
+ const hasZaiEnv = Boolean(envZaiKey);
4261
+ const apiKeyDetected = !hasApiKeyFlag && hasZaiEnv;
4262
+ const apiKey = opts["api-key"] || (providerKey === "zai" ? envZaiKey || envAnthropicKey || "" : "");
4263
+ if (apiKeyDetected && !opts.yes) {
4264
+ console.log("Detected Z_AI_API_KEY in environment. Using it by default.");
4265
+ }
4266
+ const brand = opts.brand || "auto";
4267
+ const rootDir = opts.root || DEFAULT_ROOT;
4268
+ const binDir = opts["bin-dir"] || DEFAULT_BIN_DIR;
4269
+ const npmPackage = opts["npm-package"] || DEFAULT_NPM_PACKAGE;
4270
+ const extraEnv = buildExtraEnv(opts);
4271
+ const requiresCredential = !provider.credentialOptional;
4272
+ const shouldPromptApiKey = !opts.yes && !hasApiKeyFlag && (providerKey === "zai" ? !hasZaiEnv : !apiKey);
4273
+ return {
4274
+ provider,
4275
+ providerKey,
4276
+ name,
4277
+ baseUrl,
4278
+ apiKey,
4279
+ brand,
4280
+ rootDir,
4281
+ binDir,
4282
+ npmPackage,
4283
+ extraEnv,
4284
+ requiresCredential,
4285
+ shouldPromptApiKey,
4286
+ hasZaiEnv
4287
+ };
4288
+ }
4289
+ async function handleQuickMode(opts, params) {
4290
+ const { provider } = params;
4291
+ const promptPack = opts["no-prompt-pack"] ? false : void 0;
4292
+ const promptPackMode = parsePromptPackMode(opts["prompt-pack-mode"]);
4293
+ const skillInstall = opts["no-skill-install"] ? false : void 0;
4294
+ const skillUpdate = Boolean(opts["skill-update"]);
4295
+ let shellEnv = opts["no-shell-env"] ? false : opts["shell-env"] ? true : void 0;
4296
+ const modelOverrides = getModelOverridesFromArgs(opts);
4297
+ let apiKey = params.apiKey;
4298
+ if (params.shouldPromptApiKey) {
4299
+ apiKey = params.requiresCredential ? await requirePrompt(provider.apiKeyLabel || "ANTHROPIC_API_KEY", apiKey) : await prompt(provider.apiKeyLabel || "ANTHROPIC_API_KEY", apiKey);
4300
+ }
4301
+ if (params.requiresCredential && !apiKey) {
4302
+ if (opts.yes) {
4303
+ throw new Error("Provider API key required (use --api-key)");
4304
+ }
4305
+ apiKey = await requirePrompt(provider.apiKeyLabel || "ANTHROPIC_API_KEY", apiKey);
4306
+ }
4307
+ const resolvedModelOverrides = await ensureModelMapping(params.providerKey, opts, { ...modelOverrides });
4308
+ if (params.providerKey === "zai" && shellEnv === void 0 && !opts.yes) {
4309
+ if (params.hasZaiEnv) {
4310
+ shellEnv = false;
4311
+ } else {
4312
+ const answer = await prompt("Write Z_AI_API_KEY to your shell profile? (yes/no)", "yes");
4313
+ shellEnv = answer.trim().toLowerCase().startsWith("y");
4314
+ }
4315
+ }
4316
+ const result = createVariant({
4317
+ name: params.name,
4318
+ providerKey: params.providerKey,
4319
+ baseUrl: params.baseUrl,
4320
+ apiKey,
4321
+ brand: params.brand,
4322
+ extraEnv: params.extraEnv,
4323
+ rootDir: params.rootDir,
4324
+ binDir: params.binDir,
4325
+ npmPackage: params.npmPackage,
4326
+ noTweak: Boolean(opts.noTweak),
4327
+ promptPack,
4328
+ promptPackMode,
4329
+ skillInstall,
4330
+ shellEnv,
4331
+ skillUpdate,
4332
+ modelOverrides: resolvedModelOverrides
4333
+ });
4334
+ const shareUrl = buildShareUrl(provider.label || params.providerKey, params.name, result.meta.promptPackMode);
4335
+ const modelNote = formatModelNote(resolvedModelOverrides);
4336
+ const notes = [...result.notes || [], ...modelNote ? [modelNote] : []];
4337
+ printSummary({
4338
+ action: "Created",
4339
+ meta: result.meta,
4340
+ wrapperPath: result.wrapperPath,
4341
+ notes: notes.length > 0 ? notes : void 0,
4342
+ shareUrl
4343
+ });
4344
+ }
4345
+ async function handleInteractiveMode(opts, params) {
4346
+ const { provider } = params;
4347
+ const promptPack = opts["no-prompt-pack"] ? false : void 0;
4348
+ const promptPackMode = parsePromptPackMode(opts["prompt-pack-mode"]);
4349
+ const skillInstall = opts["no-skill-install"] ? false : void 0;
4350
+ const skillUpdate = Boolean(opts["skill-update"]);
4351
+ let shellEnv = opts["no-shell-env"] ? false : opts["shell-env"] ? true : void 0;
4352
+ const modelOverrides = getModelOverridesFromArgs(opts);
4353
+ const nextName = await prompt("Variant name", params.name);
4354
+ const nextBase = await prompt("ANTHROPIC_BASE_URL", params.baseUrl);
4355
+ let nextKey = params.shouldPromptApiKey ? params.requiresCredential ? await requirePrompt(provider.apiKeyLabel || "ANTHROPIC_API_KEY", params.apiKey) : await prompt(provider.apiKeyLabel || "ANTHROPIC_API_KEY", params.apiKey) : params.apiKey;
4356
+ if (params.requiresCredential && !nextKey) {
4357
+ nextKey = await requirePrompt(provider.apiKeyLabel || "ANTHROPIC_API_KEY", params.apiKey);
4358
+ }
4359
+ const resolvedModelOverrides = await ensureModelMapping(params.providerKey, opts, { ...modelOverrides });
4360
+ const brandOptions = listBrandPresets().map((item) => item.key).join(", ");
4361
+ const brandHint = brandOptions.length > 0 ? `auto, none, ${brandOptions}` : "auto, none";
4362
+ const nextBrand = await prompt(`Brand preset (${brandHint})`, params.brand);
4363
+ const nextRoot = await prompt("Variants root directory", params.rootDir);
4364
+ const nextBin = await prompt("Wrapper install directory", params.binDir);
4365
+ const nextNpmPackage = await prompt("NPM package", params.npmPackage);
4366
+ const envInput = await prompt("Extra env (KEY=VALUE, comma separated)", params.extraEnv.join(","));
4367
+ const parsedEnv = envInput.split(",").map((entry) => entry.trim()).filter(Boolean);
4368
+ if (params.providerKey === "zai" && shellEnv === void 0) {
4369
+ if (params.hasZaiEnv) {
4370
+ shellEnv = false;
4371
+ } else {
4372
+ const answer = await prompt("Write Z_AI_API_KEY to your shell profile? (yes/no)", "yes");
4373
+ shellEnv = answer.trim().toLowerCase().startsWith("y");
4374
+ }
4375
+ }
4376
+ const result = createVariant({
4377
+ name: nextName,
4378
+ providerKey: params.providerKey,
4379
+ baseUrl: nextBase,
4380
+ apiKey: nextKey,
4381
+ brand: nextBrand,
4382
+ extraEnv: parsedEnv,
4383
+ rootDir: nextRoot,
4384
+ binDir: nextBin,
4385
+ npmPackage: nextNpmPackage,
4386
+ noTweak: Boolean(opts.noTweak),
4387
+ promptPack,
4388
+ promptPackMode,
4389
+ skillInstall,
4390
+ shellEnv,
4391
+ skillUpdate,
4392
+ modelOverrides: resolvedModelOverrides
4393
+ });
4394
+ const shareUrl = buildShareUrl(provider.label || params.providerKey, result.meta.name, result.meta.promptPackMode);
4395
+ const modelNote = formatModelNote(resolvedModelOverrides);
4396
+ const notes = [...result.notes || [], ...modelNote ? [modelNote] : []];
4397
+ printSummary({
4398
+ action: "Created",
4399
+ meta: result.meta,
4400
+ wrapperPath: result.wrapperPath,
4401
+ notes: notes.length > 0 ? notes : void 0,
4402
+ shareUrl
4403
+ });
4404
+ }
4405
+ async function handleNonInteractiveMode(opts, params) {
4406
+ const { provider } = params;
4407
+ const promptPack = opts["no-prompt-pack"] ? false : void 0;
4408
+ const promptPackMode = parsePromptPackMode(opts["prompt-pack-mode"]);
4409
+ const skillInstall = opts["no-skill-install"] ? false : void 0;
4410
+ const skillUpdate = Boolean(opts["skill-update"]);
4411
+ const shellEnv = opts["no-shell-env"] ? false : opts["shell-env"] ? true : void 0;
4412
+ const modelOverrides = getModelOverridesFromArgs(opts);
4413
+ if (params.requiresCredential && !params.apiKey) {
4414
+ throw new Error("Provider API key required (use --api-key)");
4415
+ }
4416
+ const resolvedModelOverrides = await ensureModelMapping(params.providerKey, opts, { ...modelOverrides });
4417
+ const result = createVariant({
4418
+ name: params.name,
4419
+ providerKey: params.providerKey,
4420
+ baseUrl: params.baseUrl,
4421
+ apiKey: params.apiKey,
4422
+ brand: params.brand,
4423
+ extraEnv: params.extraEnv,
4424
+ rootDir: params.rootDir,
4425
+ binDir: params.binDir,
4426
+ npmPackage: params.npmPackage,
4427
+ noTweak: Boolean(opts.noTweak),
4428
+ promptPack,
4429
+ promptPackMode,
4430
+ skillInstall,
4431
+ shellEnv,
4432
+ skillUpdate,
4433
+ modelOverrides: resolvedModelOverrides
4434
+ });
4435
+ const shareUrl = buildShareUrl(provider.label || params.providerKey, result.meta.name, result.meta.promptPackMode);
4436
+ const modelNote = formatModelNote(resolvedModelOverrides);
4437
+ const notes = [...result.notes || [], ...modelNote ? [modelNote] : []];
4438
+ printSummary({
4439
+ action: "Created",
4440
+ meta: result.meta,
4441
+ wrapperPath: result.wrapperPath,
4442
+ notes: notes.length > 0 ? notes : void 0,
4443
+ shareUrl
4444
+ });
4445
+ }
4446
+ async function runCreateCommand({ opts, quickMode }) {
4447
+ const params = await prepareCreateParams(opts);
4448
+ if (quickMode) {
4449
+ await handleQuickMode(opts, params);
4450
+ } else if (opts.yes) {
4451
+ await handleNonInteractiveMode(opts, params);
4452
+ } else {
4453
+ await handleInteractiveMode(opts, params);
4454
+ }
4455
+ }
4456
+
4457
+ // src/cli/index.ts
4458
+ var main = async () => {
4459
+ const argv = process.argv.slice(2);
4460
+ if (argv.length === 0 && process.stdout.isTTY) {
4461
+ await runTui();
4462
+ return;
4463
+ }
4464
+ let cmd = argv.length > 0 && !argv[0].startsWith("-") ? argv.shift() : "create";
4465
+ const opts = parseArgs(argv);
4466
+ const quickMode = cmd === "quick" || Boolean(opts.quick || opts.simple);
4467
+ if (cmd === "quick") cmd = "create";
4468
+ if (cmd === "help" || cmd === "--help" || opts.help) {
4469
+ printHelp();
4470
+ return;
4471
+ }
4472
+ if (opts.haiku) {
4473
+ printHaiku();
4474
+ return;
4475
+ }
4476
+ if (shouldLaunchTui(cmd, opts)) {
4477
+ await runTui();
4478
+ return;
4479
+ }
4480
+ switch (cmd) {
4481
+ case "list":
4482
+ runListCommand({ opts });
4483
+ break;
4484
+ case "doctor":
4485
+ runDoctorCommand({ opts });
4486
+ break;
4487
+ case "update":
4488
+ runUpdateCommand({ opts });
4489
+ break;
4490
+ case "remove":
4491
+ runRemoveCommand({ opts });
4492
+ break;
4493
+ case "tweak":
4494
+ runTweakCommand({ opts });
4495
+ break;
4496
+ case "create":
4497
+ await runCreateCommand({ opts, quickMode });
4498
+ break;
4499
+ default:
4500
+ printHelp();
4501
+ }
4502
+ };
4503
+ main().catch((error) => {
4504
+ console.error(error instanceof Error ? error.message : String(error));
4505
+ process.exit(1);
4506
+ });