@xwm111/ccs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/bin/ccs.mjs +2 -0
  4. package/dist/chunks/auto-updater.mjs +1708 -0
  5. package/dist/chunks/claude-code-incremental-manager.mjs +576 -0
  6. package/dist/chunks/installer.mjs +610 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.d.ts +1 -0
  9. package/dist/cli.mjs +2407 -0
  10. package/dist/i18n/locales/en/api.json +51 -0
  11. package/dist/i18n/locales/en/cli.json +58 -0
  12. package/dist/i18n/locales/en/common.json +19 -0
  13. package/dist/i18n/locales/en/configuration.json +81 -0
  14. package/dist/i18n/locales/en/errors.json +26 -0
  15. package/dist/i18n/locales/en/installation.json +80 -0
  16. package/dist/i18n/locales/en/language.json +19 -0
  17. package/dist/i18n/locales/en/menu.json +31 -0
  18. package/dist/i18n/locales/en/multi-config.json +79 -0
  19. package/dist/i18n/locales/en/uninstall.json +56 -0
  20. package/dist/i18n/locales/en/updater.json +26 -0
  21. package/dist/i18n/locales/zh-CN/api.json +51 -0
  22. package/dist/i18n/locales/zh-CN/cli.json +58 -0
  23. package/dist/i18n/locales/zh-CN/common.json +19 -0
  24. package/dist/i18n/locales/zh-CN/configuration.json +81 -0
  25. package/dist/i18n/locales/zh-CN/errors.json +26 -0
  26. package/dist/i18n/locales/zh-CN/installation.json +80 -0
  27. package/dist/i18n/locales/zh-CN/language.json +19 -0
  28. package/dist/i18n/locales/zh-CN/menu.json +31 -0
  29. package/dist/i18n/locales/zh-CN/multi-config.json +79 -0
  30. package/dist/i18n/locales/zh-CN/uninstall.json +56 -0
  31. package/dist/i18n/locales/zh-CN/updater.json +26 -0
  32. package/dist/index.d.mts +222 -0
  33. package/dist/index.d.ts +222 -0
  34. package/dist/index.mjs +18 -0
  35. package/package.json +109 -0
@@ -0,0 +1,576 @@
1
+ import ansis from 'ansis';
2
+ import inquirer from 'inquirer';
3
+ import { G as ensureI18nInitialized, H as i18n, W as promptBoolean } from './auto-updater.mjs';
4
+ import { C as ClaudeCodeConfigManager, a as addNumbersToChoices } from '../cli.mjs';
5
+ import 'node:url';
6
+ import 'dayjs';
7
+ import 'pathe';
8
+ import 'node:os';
9
+ import 'node:fs';
10
+ import 'node:process';
11
+ import 'tinyexec';
12
+ import 'ora';
13
+ import 'semver';
14
+ import 'i18next';
15
+ import 'i18next-fs-backend';
16
+ import 'inquirer-toggle';
17
+ import 'node:child_process';
18
+ import 'node:util';
19
+ import 'cac';
20
+ import '@rainbowatcher/toml-edit-js';
21
+ import 'fs-extra';
22
+ import 'trash';
23
+
24
+ function validateApiKey(apiKey) {
25
+ if (!apiKey || apiKey.trim() === "") {
26
+ return {
27
+ isValid: false,
28
+ // Note: This should use i18next, but due to sync constraint in inquirer validate,
29
+ // we temporarily use a generic message. This will be fixed when we refactor to async validation.
30
+ error: "API key cannot be empty"
31
+ };
32
+ }
33
+ return { isValid: true };
34
+ }
35
+
36
+ function getAuthTypeLabel(authType) {
37
+ ensureI18nInitialized();
38
+ switch (authType) {
39
+ case "api_key":
40
+ return i18n.t("multi-config:authType.api_key");
41
+ case "auth_token":
42
+ return i18n.t("multi-config:authType.auth_token");
43
+ case "ccr_proxy":
44
+ return i18n.t("multi-config:authType.ccr_proxy");
45
+ default:
46
+ return authType;
47
+ }
48
+ }
49
+ async function configureIncrementalManagement() {
50
+ ensureI18nInitialized();
51
+ const config = ClaudeCodeConfigManager.readConfig();
52
+ if (!config || !config.profiles || Object.keys(config.profiles).length === 0) {
53
+ await handleAddProfile();
54
+ return;
55
+ }
56
+ const profiles = Object.values(config.profiles);
57
+ const currentProfile = config.currentProfileId ? config.profiles[config.currentProfileId] : null;
58
+ console.log(ansis.cyan(i18n.t("multi-config:incrementalManagementTitle")));
59
+ console.log(ansis.gray(i18n.t("multi-config:currentProfileCount", { count: profiles.length })));
60
+ if (currentProfile) {
61
+ console.log(ansis.gray(i18n.t("multi-config:currentDefaultProfile", { profile: currentProfile.name })));
62
+ }
63
+ const choices = [
64
+ { name: i18n.t("multi-config:addProfile"), value: "add" },
65
+ { name: i18n.t("multi-config:editProfile"), value: "edit" },
66
+ { name: i18n.t("multi-config:copyProfile"), value: "copy" },
67
+ { name: i18n.t("multi-config:deleteProfile"), value: "delete" },
68
+ { name: i18n.t("common:skip"), value: "skip" }
69
+ ];
70
+ const { action } = await inquirer.prompt([{
71
+ type: "list",
72
+ name: "action",
73
+ message: i18n.t("multi-config:selectAction"),
74
+ choices: addNumbersToChoices(choices)
75
+ }]);
76
+ if (!action || action === "skip") {
77
+ console.log(ansis.yellow(i18n.t("common:skip")));
78
+ return;
79
+ }
80
+ switch (action) {
81
+ case "add":
82
+ await handleAddProfile();
83
+ break;
84
+ case "edit":
85
+ await handleEditProfile(profiles);
86
+ break;
87
+ case "copy":
88
+ await handleCopyProfile(profiles);
89
+ break;
90
+ case "delete":
91
+ await handleDeleteProfile(profiles);
92
+ break;
93
+ }
94
+ }
95
+ async function promptContinueAdding() {
96
+ return await promptBoolean({
97
+ message: i18n.t("multi-config:addAnotherProfilePrompt"),
98
+ defaultValue: false
99
+ });
100
+ }
101
+ async function handleAddProfile() {
102
+ console.log(ansis.cyan(`
103
+ ${i18n.t("multi-config:addingNewProfile")}`));
104
+ const answers = await inquirer.prompt([
105
+ {
106
+ type: "input",
107
+ name: "profileName",
108
+ message: i18n.t("multi-config:profileNamePrompt"),
109
+ validate: (input) => {
110
+ const trimmed = input.trim();
111
+ if (!trimmed) {
112
+ return i18n.t("multi-config:profileNameRequired");
113
+ }
114
+ if (!/^[\w\-\s.\u4E00-\u9FA5]+$/.test(trimmed)) {
115
+ return i18n.t("multi-config:profileNameInvalid");
116
+ }
117
+ return true;
118
+ }
119
+ },
120
+ {
121
+ type: "list",
122
+ name: "authType",
123
+ message: i18n.t("multi-config:authTypePrompt"),
124
+ choices: [
125
+ { name: i18n.t("multi-config:authType.api_key"), value: "api_key" },
126
+ { name: i18n.t("multi-config:authType.auth_token"), value: "auth_token" }
127
+ ],
128
+ default: "api_key"
129
+ },
130
+ {
131
+ type: "input",
132
+ name: "baseUrl",
133
+ message: i18n.t("multi-config:baseUrlPrompt"),
134
+ default: "https://api.anthropic.com",
135
+ when: (answers2) => answers2.authType !== "ccr_proxy",
136
+ validate: (input) => {
137
+ const trimmed = input.trim();
138
+ if (!trimmed) {
139
+ return i18n.t("multi-config:baseUrlRequired");
140
+ }
141
+ try {
142
+ new URL(trimmed);
143
+ return true;
144
+ } catch {
145
+ return i18n.t("multi-config:baseUrlInvalid");
146
+ }
147
+ }
148
+ },
149
+ {
150
+ type: "input",
151
+ name: "apiKey",
152
+ message: i18n.t("multi-config:apiKeyPrompt"),
153
+ when: (answers2) => answers2.authType !== "ccr_proxy",
154
+ validate: (input) => {
155
+ const trimmed = input.trim();
156
+ if (!trimmed) {
157
+ return i18n.t("multi-config:apiKeyRequired");
158
+ }
159
+ const validation = validateApiKey(trimmed);
160
+ if (!validation.isValid) {
161
+ return validation.error || "Invalid API key format";
162
+ }
163
+ return true;
164
+ }
165
+ }
166
+ ]);
167
+ const { promptCustomModels } = await import('../cli.mjs').then(function (n) { return n.f; });
168
+ const modelConfig = await promptCustomModels();
169
+ const setAsDefault = await promptBoolean({
170
+ message: i18n.t("multi-config:setAsDefaultPrompt"),
171
+ defaultValue: true
172
+ });
173
+ const profileName = answers.profileName.trim();
174
+ const profileId = ClaudeCodeConfigManager.generateProfileId(profileName);
175
+ const profile = {
176
+ id: profileId,
177
+ name: profileName,
178
+ authType: answers.authType
179
+ };
180
+ if (profile.authType !== "ccr_proxy") {
181
+ profile.apiKey = answers.apiKey.trim();
182
+ profile.baseUrl = answers.baseUrl.trim();
183
+ }
184
+ if (modelConfig) {
185
+ if (modelConfig.primaryModel.trim()) {
186
+ profile.primaryModel = modelConfig.primaryModel.trim();
187
+ }
188
+ if (modelConfig.haikuModel.trim())
189
+ profile.defaultHaikuModel = modelConfig.haikuModel.trim();
190
+ if (modelConfig.sonnetModel.trim())
191
+ profile.defaultSonnetModel = modelConfig.sonnetModel.trim();
192
+ if (modelConfig.opusModel.trim())
193
+ profile.defaultOpusModel = modelConfig.opusModel.trim();
194
+ }
195
+ const existingProfile = ClaudeCodeConfigManager.getProfileByName(profile.name);
196
+ if (existingProfile) {
197
+ const overwrite = await promptBoolean({
198
+ message: i18n.t("multi-config:profileDuplicatePrompt", {
199
+ name: profile.name,
200
+ source: i18n.t("multi-config:existingConfig")
201
+ }),
202
+ defaultValue: false
203
+ });
204
+ if (!overwrite) {
205
+ console.log(ansis.yellow(i18n.t("multi-config:profileDuplicateSkipped", { name: profile.name })));
206
+ const shouldContinue2 = await promptContinueAdding();
207
+ if (shouldContinue2) {
208
+ await handleAddProfile();
209
+ }
210
+ return;
211
+ }
212
+ const updateResult = await ClaudeCodeConfigManager.updateProfile(existingProfile.id, {
213
+ name: profile.name,
214
+ authType: profile.authType,
215
+ apiKey: profile.apiKey,
216
+ baseUrl: profile.baseUrl,
217
+ primaryModel: profile.primaryModel,
218
+ defaultHaikuModel: profile.defaultHaikuModel,
219
+ defaultSonnetModel: profile.defaultSonnetModel,
220
+ defaultOpusModel: profile.defaultOpusModel
221
+ });
222
+ if (updateResult.success) {
223
+ console.log(ansis.green(i18n.t("multi-config:profileUpdated", { name: profile.name })));
224
+ if (updateResult.backupPath) {
225
+ console.log(ansis.gray(i18n.t("common:backupCreated", { path: updateResult.backupPath })));
226
+ }
227
+ if (setAsDefault) {
228
+ const switchResult = await ClaudeCodeConfigManager.switchProfile(existingProfile.id);
229
+ if (switchResult.success) {
230
+ console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: profile.name })));
231
+ await ClaudeCodeConfigManager.applyProfileSettings({ ...profile, id: existingProfile.id });
232
+ }
233
+ }
234
+ } else {
235
+ console.log(ansis.red(i18n.t("multi-config:profileUpdateFailed", { error: updateResult.error || "" })));
236
+ }
237
+ } else {
238
+ const result = await ClaudeCodeConfigManager.addProfile(profile);
239
+ if (result.success) {
240
+ const runtimeProfile = result.addedProfile || { ...profile, id: profileId };
241
+ console.log(ansis.green(i18n.t("multi-config:profileAdded", { name: runtimeProfile.name })));
242
+ if (result.backupPath) {
243
+ console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath })));
244
+ }
245
+ if (setAsDefault) {
246
+ const switchResult = await ClaudeCodeConfigManager.switchProfile(runtimeProfile.id);
247
+ if (switchResult.success) {
248
+ console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: runtimeProfile.name })));
249
+ await ClaudeCodeConfigManager.applyProfileSettings(runtimeProfile);
250
+ }
251
+ }
252
+ } else {
253
+ console.log(ansis.red(i18n.t("multi-config:profileAddFailed", { error: result.error })));
254
+ }
255
+ }
256
+ const shouldContinue = await promptContinueAdding();
257
+ if (shouldContinue) {
258
+ await handleAddProfile();
259
+ }
260
+ }
261
+ async function handleEditProfile(profiles) {
262
+ const choices = profiles.map((profile) => ({
263
+ name: `${profile.name} (${getAuthTypeLabel(profile.authType)})`,
264
+ value: profile.id
265
+ }));
266
+ const { selectedProfileId } = await inquirer.prompt([{
267
+ type: "list",
268
+ name: "selectedProfileId",
269
+ message: i18n.t("multi-config:selectProfileToEdit"),
270
+ choices: addNumbersToChoices(choices)
271
+ }]);
272
+ if (!selectedProfileId) {
273
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
274
+ return;
275
+ }
276
+ const selectedProfile = profiles.find((p) => p.id === selectedProfileId);
277
+ if (!selectedProfile) {
278
+ console.log(ansis.red(i18n.t("multi-config:profileNotFound")));
279
+ return;
280
+ }
281
+ console.log(ansis.cyan(`
282
+ ${i18n.t("multi-config:editingProfile", { name: selectedProfile.name })}`));
283
+ const answers = await inquirer.prompt([
284
+ {
285
+ type: "input",
286
+ name: "profileName",
287
+ message: i18n.t("multi-config:profileNamePrompt"),
288
+ default: selectedProfile.name,
289
+ validate: (input) => {
290
+ const trimmed = input.trim();
291
+ if (!trimmed) {
292
+ return i18n.t("multi-config:profileNameRequired");
293
+ }
294
+ if (!/^[\w\-\s\u4E00-\u9FA5]+$/.test(trimmed)) {
295
+ return i18n.t("multi-config:profileNameInvalid");
296
+ }
297
+ return true;
298
+ }
299
+ },
300
+ {
301
+ type: "input",
302
+ name: "baseUrl",
303
+ message: i18n.t("multi-config:baseUrlPrompt"),
304
+ default: selectedProfile.baseUrl || "https://api.anthropic.com",
305
+ when: () => selectedProfile.authType !== "ccr_proxy",
306
+ validate: (input) => {
307
+ const trimmed = input.trim();
308
+ if (!trimmed) {
309
+ return i18n.t("multi-config:baseUrlRequired");
310
+ }
311
+ try {
312
+ new URL(trimmed);
313
+ return true;
314
+ } catch {
315
+ return i18n.t("multi-config:baseUrlInvalid");
316
+ }
317
+ }
318
+ },
319
+ {
320
+ type: "input",
321
+ name: "apiKey",
322
+ message: i18n.t("multi-config:apiKeyPrompt"),
323
+ default: selectedProfile.apiKey,
324
+ when: () => selectedProfile.authType !== "ccr_proxy",
325
+ validate: (input) => {
326
+ const trimmed = input.trim();
327
+ if (!trimmed) {
328
+ return i18n.t("multi-config:apiKeyRequired");
329
+ }
330
+ const validation = validateApiKey(trimmed);
331
+ if (!validation.isValid) {
332
+ return validation.error || "Invalid API key format";
333
+ }
334
+ return true;
335
+ }
336
+ }
337
+ ]);
338
+ let modelConfig = null;
339
+ if (selectedProfile.authType !== "ccr_proxy") {
340
+ const { promptCustomModels } = await import('../cli.mjs').then(function (n) { return n.f; });
341
+ modelConfig = await promptCustomModels(
342
+ selectedProfile.primaryModel,
343
+ selectedProfile.defaultHaikuModel,
344
+ selectedProfile.defaultSonnetModel,
345
+ selectedProfile.defaultOpusModel
346
+ );
347
+ }
348
+ const updateData = {
349
+ name: answers.profileName.trim()
350
+ };
351
+ if (selectedProfile.authType !== "ccr_proxy") {
352
+ updateData.apiKey = answers.apiKey.trim();
353
+ updateData.baseUrl = answers.baseUrl.trim();
354
+ if (modelConfig) {
355
+ if (modelConfig.primaryModel.trim()) {
356
+ updateData.primaryModel = modelConfig.primaryModel.trim();
357
+ }
358
+ if (modelConfig.haikuModel.trim())
359
+ updateData.defaultHaikuModel = modelConfig.haikuModel.trim();
360
+ if (modelConfig.sonnetModel.trim())
361
+ updateData.defaultSonnetModel = modelConfig.sonnetModel.trim();
362
+ if (modelConfig.opusModel.trim())
363
+ updateData.defaultOpusModel = modelConfig.opusModel.trim();
364
+ }
365
+ }
366
+ const result = await ClaudeCodeConfigManager.updateProfile(selectedProfile.id, updateData);
367
+ if (result.success) {
368
+ console.log(ansis.green(i18n.t("multi-config:profileUpdated", { name: updateData.name })));
369
+ if (result.backupPath) {
370
+ console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath })));
371
+ }
372
+ const currentConfig = ClaudeCodeConfigManager.readConfig();
373
+ if (currentConfig?.currentProfileId === selectedProfile.id) {
374
+ const updatedProfile = ClaudeCodeConfigManager.getProfileById(selectedProfile.id);
375
+ if (updatedProfile) {
376
+ await ClaudeCodeConfigManager.applyProfileSettings(updatedProfile);
377
+ console.log(ansis.green(i18n.t("multi-config:settingsApplied")));
378
+ }
379
+ }
380
+ } else {
381
+ console.log(ansis.red(i18n.t("multi-config:profileUpdateFailed", { error: result.error })));
382
+ }
383
+ }
384
+ async function handleCopyProfile(profiles) {
385
+ const choices = profiles.map((profile) => ({
386
+ name: `${profile.name} (${getAuthTypeLabel(profile.authType)})`,
387
+ value: profile.id
388
+ }));
389
+ const { selectedProfileId } = await inquirer.prompt([{
390
+ type: "list",
391
+ name: "selectedProfileId",
392
+ message: i18n.t("multi-config:selectProfileToCopy"),
393
+ choices: addNumbersToChoices(choices)
394
+ }]);
395
+ if (!selectedProfileId) {
396
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
397
+ return;
398
+ }
399
+ const selectedProfile = profiles.find((p) => p.id === selectedProfileId);
400
+ if (!selectedProfile) {
401
+ console.log(ansis.red(i18n.t("multi-config:profileNotFound")));
402
+ return;
403
+ }
404
+ console.log(ansis.cyan(`
405
+ ${i18n.t("multi-config:copyingProfile", { name: selectedProfile.name })}`));
406
+ const copiedName = `${selectedProfile.name}-copy`;
407
+ const questions = [
408
+ {
409
+ type: "input",
410
+ name: "profileName",
411
+ message: i18n.t("multi-config:profileNamePrompt"),
412
+ default: copiedName,
413
+ validate: (input) => {
414
+ const trimmed = input.trim();
415
+ if (!trimmed) {
416
+ return i18n.t("multi-config:profileNameRequired");
417
+ }
418
+ if (!/^[\w\-\s.\u4E00-\u9FA5]+$/.test(trimmed)) {
419
+ return i18n.t("multi-config:profileNameInvalid");
420
+ }
421
+ return true;
422
+ }
423
+ }
424
+ ];
425
+ if (selectedProfile.authType !== "ccr_proxy") {
426
+ questions.push(
427
+ {
428
+ type: "input",
429
+ name: "baseUrl",
430
+ message: i18n.t("multi-config:baseUrlPrompt"),
431
+ default: selectedProfile.baseUrl || "https://api.anthropic.com",
432
+ validate: (input) => {
433
+ const trimmed = input.trim();
434
+ if (!trimmed) {
435
+ return i18n.t("multi-config:baseUrlRequired");
436
+ }
437
+ try {
438
+ new URL(trimmed);
439
+ return true;
440
+ } catch {
441
+ return i18n.t("multi-config:baseUrlInvalid");
442
+ }
443
+ }
444
+ },
445
+ {
446
+ type: "input",
447
+ name: "apiKey",
448
+ message: i18n.t("multi-config:apiKeyPrompt"),
449
+ default: selectedProfile.apiKey,
450
+ validate: (input) => {
451
+ const trimmed = input.trim();
452
+ if (!trimmed) {
453
+ return i18n.t("multi-config:apiKeyRequired");
454
+ }
455
+ const validation = validateApiKey(trimmed);
456
+ if (!validation.isValid) {
457
+ return validation.error || "Invalid API key format";
458
+ }
459
+ return true;
460
+ }
461
+ }
462
+ );
463
+ }
464
+ const answers = await inquirer.prompt(questions);
465
+ let modelConfig = null;
466
+ if (selectedProfile.authType !== "ccr_proxy") {
467
+ const { promptCustomModels } = await import('../cli.mjs').then(function (n) { return n.f; });
468
+ modelConfig = await promptCustomModels(
469
+ selectedProfile.primaryModel,
470
+ selectedProfile.defaultHaikuModel,
471
+ selectedProfile.defaultSonnetModel,
472
+ selectedProfile.defaultOpusModel
473
+ );
474
+ }
475
+ const setAsDefault = await promptBoolean({
476
+ message: i18n.t("multi-config:setAsDefaultPrompt"),
477
+ defaultValue: false
478
+ });
479
+ const profileName = answers.profileName.trim();
480
+ const profileId = ClaudeCodeConfigManager.generateProfileId(profileName);
481
+ const copiedProfile = {
482
+ id: profileId,
483
+ name: profileName,
484
+ authType: selectedProfile.authType
485
+ };
486
+ if (selectedProfile.authType !== "ccr_proxy") {
487
+ copiedProfile.apiKey = answers.apiKey.trim();
488
+ copiedProfile.baseUrl = answers.baseUrl.trim();
489
+ if (modelConfig) {
490
+ if (modelConfig.primaryModel.trim()) {
491
+ copiedProfile.primaryModel = modelConfig.primaryModel.trim();
492
+ }
493
+ if (modelConfig.haikuModel.trim())
494
+ copiedProfile.defaultHaikuModel = modelConfig.haikuModel.trim();
495
+ if (modelConfig.sonnetModel.trim())
496
+ copiedProfile.defaultSonnetModel = modelConfig.sonnetModel.trim();
497
+ if (modelConfig.opusModel.trim())
498
+ copiedProfile.defaultOpusModel = modelConfig.opusModel.trim();
499
+ }
500
+ }
501
+ const result = await ClaudeCodeConfigManager.addProfile(copiedProfile);
502
+ if (result.success) {
503
+ const runtimeProfile = result.addedProfile || { ...copiedProfile, id: profileId };
504
+ console.log(ansis.green(i18n.t("multi-config:profileCopied", { name: runtimeProfile.name })));
505
+ if (result.backupPath) {
506
+ console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath })));
507
+ }
508
+ if (setAsDefault) {
509
+ const switchResult = await ClaudeCodeConfigManager.switchProfile(runtimeProfile.id);
510
+ if (switchResult.success) {
511
+ console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: runtimeProfile.name })));
512
+ await ClaudeCodeConfigManager.applyProfileSettings(runtimeProfile);
513
+ }
514
+ }
515
+ } else {
516
+ console.log(ansis.red(i18n.t("multi-config:profileCopyFailed", { error: result.error })));
517
+ }
518
+ }
519
+ async function handleDeleteProfile(profiles) {
520
+ if (profiles.length <= 1) {
521
+ console.log(ansis.yellow(i18n.t("multi-config:cannotDeleteLast")));
522
+ return;
523
+ }
524
+ const choices = profiles.map((profile) => ({
525
+ name: `${profile.name} (${getAuthTypeLabel(profile.authType)})`,
526
+ value: profile.id
527
+ }));
528
+ const { selectedProfileIds } = await inquirer.prompt({
529
+ type: "checkbox",
530
+ name: "selectedProfileIds",
531
+ message: i18n.t("multi-config:selectProfilesToDelete"),
532
+ choices: addNumbersToChoices(choices),
533
+ validate: (input) => {
534
+ if (input.length === 0) {
535
+ return i18n.t("multi-config:selectAtLeastOne");
536
+ }
537
+ if (input.length >= profiles.length) {
538
+ return i18n.t("multi-config:cannotDeleteAll");
539
+ }
540
+ return true;
541
+ }
542
+ });
543
+ if (!selectedProfileIds || selectedProfileIds.length === 0) {
544
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
545
+ return;
546
+ }
547
+ const selectedNames = selectedProfileIds.map(
548
+ (id) => profiles.find((p) => p.id === id)?.name || id
549
+ );
550
+ const confirmed = await promptBoolean({
551
+ message: i18n.t("multi-config:confirmDeleteProfiles", { providers: selectedNames.join(", ") }),
552
+ defaultValue: false
553
+ });
554
+ if (!confirmed) {
555
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
556
+ return;
557
+ }
558
+ const result = await ClaudeCodeConfigManager.deleteProfiles(selectedProfileIds);
559
+ if (result.success) {
560
+ console.log(ansis.green(i18n.t("multi-config:profilesDeleted", { count: selectedProfileIds.length })));
561
+ if (result.backupPath) {
562
+ console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath })));
563
+ }
564
+ if (result.newCurrentProfileId) {
565
+ const newProfile = ClaudeCodeConfigManager.getProfileById(result.newCurrentProfileId);
566
+ if (newProfile) {
567
+ console.log(ansis.cyan(i18n.t("multi-config:newDefaultProfile", { profile: newProfile.name })));
568
+ await ClaudeCodeConfigManager.applyProfileSettings(newProfile);
569
+ }
570
+ }
571
+ } else {
572
+ console.log(ansis.red(i18n.t("multi-config:profilesDeleteFailed", { error: result.error })));
573
+ }
574
+ }
575
+
576
+ export { configureIncrementalManagement, getAuthTypeLabel };