ccjk 9.6.1 → 9.8.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 (56) hide show
  1. package/dist/chunks/boost.mjs +246 -7
  2. package/dist/chunks/ccjk-mcp.mjs +1 -1
  3. package/dist/chunks/ccr.mjs +25 -28
  4. package/dist/chunks/check-updates.mjs +4 -3
  5. package/dist/chunks/claude-code-config-manager.mjs +1 -1
  6. package/dist/chunks/claude-code-incremental-manager.mjs +1 -1
  7. package/dist/chunks/claude-config.mjs +1 -1
  8. package/dist/chunks/codex-config-switch.mjs +3 -4
  9. package/dist/chunks/codex-provider-manager.mjs +1 -2
  10. package/dist/chunks/codex.mjs +204 -3
  11. package/dist/chunks/config-switch.mjs +2 -3
  12. package/dist/chunks/config.mjs +1 -1
  13. package/dist/chunks/doctor.mjs +1 -1
  14. package/dist/chunks/features.mjs +24 -15
  15. package/dist/chunks/hook-installer.mjs +44 -0
  16. package/dist/chunks/index3.mjs +32 -32
  17. package/dist/chunks/init.mjs +129 -87
  18. package/dist/chunks/installer2.mjs +1 -1
  19. package/dist/chunks/interview.mjs +1 -1
  20. package/dist/chunks/mcp.mjs +1058 -17
  21. package/dist/chunks/menu.mjs +140 -56
  22. package/dist/chunks/package.mjs +2 -210
  23. package/dist/chunks/platform.mjs +1 -1
  24. package/dist/chunks/quick-setup.mjs +35 -18
  25. package/dist/chunks/simple-config.mjs +1 -1
  26. package/dist/{shared/ccjk.q1koQxEE.mjs → chunks/smart-defaults.mjs} +77 -79
  27. package/dist/chunks/status.mjs +208 -101
  28. package/dist/chunks/thinking.mjs +1 -1
  29. package/dist/chunks/uninstall.mjs +6 -4
  30. package/dist/chunks/update.mjs +4 -7
  31. package/dist/chunks/version-checker.mjs +1 -1
  32. package/dist/cli.mjs +4 -80
  33. package/dist/index.d.mts +17 -1482
  34. package/dist/index.d.ts +17 -1482
  35. package/dist/index.mjs +12 -4191
  36. package/dist/shared/{ccjk.CSkyCZIM.mjs → ccjk.Bndhan7G.mjs} +4 -242
  37. package/dist/shared/ccjk.CeE8RLG2.mjs +62 -0
  38. package/dist/shared/ccjk.DKojSRzw.mjs +266 -0
  39. package/dist/shared/{ccjk.CItD1fpl.mjs → ccjk.DvIrK0wz.mjs} +1 -1
  40. package/dist/shared/ccjk.LsPZ2PYo.mjs +1048 -0
  41. package/package.json +1 -1
  42. package/dist/chunks/api-adapter.mjs +0 -180
  43. package/dist/chunks/cli.mjs +0 -2227
  44. package/dist/chunks/context-menu.mjs +0 -913
  45. package/dist/chunks/hooks-sync.mjs +0 -1627
  46. package/dist/chunks/mcp-market.mjs +0 -1077
  47. package/dist/chunks/mcp-server.mjs +0 -776
  48. package/dist/chunks/project-detector.mjs +0 -131
  49. package/dist/chunks/provider-registry.mjs +0 -92
  50. package/dist/chunks/setup-wizard.mjs +0 -362
  51. package/dist/chunks/tools.mjs +0 -143
  52. package/dist/chunks/workflows2.mjs +0 -232
  53. package/dist/shared/ccjk.C0pb50xH.mjs +0 -347
  54. package/dist/shared/ccjk.ChMkBmdL.mjs +0 -490
  55. package/dist/shared/ccjk.CtSfXUSh.mjs +0 -209
  56. package/dist/shared/ccjk.xfAjmbJp.mjs +0 -75
@@ -1,490 +0,0 @@
1
- import ansis from 'ansis';
2
- import inquirer from 'inquirer';
3
- import { CLAUDE_DIR, SETTINGS_FILE } from '../chunks/constants.mjs';
4
- import { ensureI18nInitialized, i18n } from '../chunks/index.mjs';
5
- import { m as mergeAndCleanPermissions, g as getExistingApiConfig, h as configureApi, s as switchToOfficialLogin, b as backupExistingConfig, d as applyAiLanguageDirective } from '../chunks/config.mjs';
6
- import { fileURLToPath } from 'node:url';
7
- import { join, dirname } from 'pathe';
8
- import { updateZcfConfig } from '../chunks/ccjk-config.mjs';
9
- import { exists, removeFile, ensureDir, copyFile } from '../chunks/fs-operations.mjs';
10
- import { readJsonConfig, writeJsonConfig } from '../chunks/json-config.mjs';
11
- import { a as addNumbersToChoices } from './ccjk.BFQ7yr5S.mjs';
12
- import { p as promptBoolean } from './ccjk.DHbrGcgg.mjs';
13
-
14
- const OUTPUT_STYLES = [
15
- // Custom styles (have template files) - 大神模式
16
- {
17
- id: "linus-mode",
18
- isCustom: true,
19
- filePath: "linus-mode.md"
20
- },
21
- {
22
- id: "uncle-bob-mode",
23
- isCustom: true,
24
- filePath: "uncle-bob-mode.md"
25
- },
26
- {
27
- id: "dhh-mode",
28
- isCustom: true,
29
- filePath: "dhh-mode.md"
30
- },
31
- {
32
- id: "carmack-mode",
33
- isCustom: true,
34
- filePath: "carmack-mode.md"
35
- },
36
- {
37
- id: "jobs-mode",
38
- isCustom: true,
39
- filePath: "jobs-mode.md"
40
- },
41
- {
42
- id: "evan-you-mode",
43
- isCustom: true,
44
- filePath: "evan-you-mode.md"
45
- },
46
- // Built-in styles (no template files) - Claude Code native styles
47
- {
48
- id: "default",
49
- isCustom: false
50
- },
51
- {
52
- id: "explanatory",
53
- isCustom: false
54
- },
55
- {
56
- id: "learning",
57
- isCustom: false
58
- }
59
- ];
60
- const LEGACY_FILES = ["personality.md", "rules.md", "technical-guides.md", "mcp.md", "language.md"];
61
- function getAvailableOutputStyles() {
62
- return OUTPUT_STYLES;
63
- }
64
- async function copyOutputStyles(selectedStyles, lang) {
65
- const outputStylesDir = join(CLAUDE_DIR, "output-styles");
66
- ensureDir(outputStylesDir);
67
- const currentFilePath = fileURLToPath(import.meta.url);
68
- const distDir = dirname(dirname(currentFilePath));
69
- const rootDir = dirname(distDir);
70
- const templateDir = join(rootDir, "templates", "common", "output-styles", lang);
71
- for (const styleId of selectedStyles) {
72
- const style = OUTPUT_STYLES.find((s) => s.id === styleId);
73
- if (!style || !style.isCustom || !style.filePath) {
74
- continue;
75
- }
76
- const sourcePath = join(templateDir, style.filePath);
77
- const destPath = join(outputStylesDir, style.filePath);
78
- if (exists(sourcePath)) {
79
- copyFile(sourcePath, destPath);
80
- }
81
- }
82
- }
83
- function setGlobalDefaultOutputStyle(styleId) {
84
- const templatePermissions = getTemplatePermissions();
85
- const existingSettings = readJsonConfig(SETTINGS_FILE) || {};
86
- const cleanedPermissions = mergeAndCleanPermissions(
87
- templatePermissions,
88
- existingSettings.permissions?.allow
89
- );
90
- const updatedSettings = {
91
- ...existingSettings,
92
- outputStyle: styleId,
93
- // Ensure clean permissions
94
- permissions: {
95
- allow: cleanedPermissions
96
- }
97
- };
98
- if (updatedSettings.plansDirectory === null) {
99
- delete updatedSettings.plansDirectory;
100
- }
101
- writeJsonConfig(SETTINGS_FILE, updatedSettings);
102
- }
103
- function getTemplatePermissions() {
104
- try {
105
- const { readFileSync } = require("node:fs");
106
- const { fileURLToPath: fileURLToPath2 } = require("node:url");
107
- const { dirname: dirname2, join: join2 } = require("pathe");
108
- const currentFilePath = fileURLToPath2(import.meta.url);
109
- const distDir = dirname2(dirname2(currentFilePath));
110
- const rootDir = dirname2(distDir);
111
- const templatePath = join2(rootDir, "templates", "claude-code", "common", "settings.json");
112
- if (require("node:fs").existsSync(templatePath)) {
113
- const template = JSON.parse(readFileSync(templatePath, "utf-8"));
114
- return template.permissions?.allow || [];
115
- }
116
- } catch (_error) {
117
- }
118
- return [
119
- "AllowEdit",
120
- "AllowWrite",
121
- "AllowRead",
122
- "AllowExec",
123
- "AllowCreateProcess",
124
- "AllowKillProcess",
125
- "AllowNetworkAccess",
126
- "AllowFileSystemAccess",
127
- "AllowShellAccess",
128
- "AllowHttpAccess"
129
- ];
130
- }
131
- function hasLegacyPersonalityFiles() {
132
- return LEGACY_FILES.some((filename) => exists(join(CLAUDE_DIR, filename)));
133
- }
134
- function cleanupLegacyPersonalityFiles() {
135
- LEGACY_FILES.forEach((filename) => {
136
- const filePath = join(CLAUDE_DIR, filename);
137
- if (exists(filePath)) {
138
- removeFile(filePath);
139
- }
140
- });
141
- }
142
- async function configureOutputStyle(preselectedStyles, preselectedDefault) {
143
- ensureI18nInitialized();
144
- const outputStyleList = [
145
- {
146
- id: "default",
147
- name: i18n.t("configuration:outputStyles.default.name"),
148
- description: i18n.t("configuration:outputStyles.default.description")
149
- },
150
- {
151
- id: "linus-mode",
152
- name: i18n.t("configuration:outputStyles.linus-mode.name"),
153
- description: i18n.t("configuration:outputStyles.linus-mode.description")
154
- },
155
- {
156
- id: "uncle-bob-mode",
157
- name: i18n.t("configuration:outputStyles.uncle-bob-mode.name"),
158
- description: i18n.t("configuration:outputStyles.uncle-bob-mode.description")
159
- },
160
- {
161
- id: "dhh-mode",
162
- name: i18n.t("configuration:outputStyles.dhh-mode.name"),
163
- description: i18n.t("configuration:outputStyles.dhh-mode.description")
164
- },
165
- {
166
- id: "carmack-mode",
167
- name: i18n.t("configuration:outputStyles.carmack-mode.name"),
168
- description: i18n.t("configuration:outputStyles.carmack-mode.description")
169
- },
170
- {
171
- id: "jobs-mode",
172
- name: i18n.t("configuration:outputStyles.jobs-mode.name"),
173
- description: i18n.t("configuration:outputStyles.jobs-mode.description")
174
- },
175
- {
176
- id: "evan-you-mode",
177
- name: i18n.t("configuration:outputStyles.evan-you-mode.name"),
178
- description: i18n.t("configuration:outputStyles.evan-you-mode.description")
179
- },
180
- {
181
- id: "explanatory",
182
- name: i18n.t("configuration:outputStyles.explanatory.name"),
183
- description: i18n.t("configuration:outputStyles.explanatory.description")
184
- },
185
- {
186
- id: "learning",
187
- name: i18n.t("configuration:outputStyles.learning.name"),
188
- description: i18n.t("configuration:outputStyles.learning.description")
189
- }
190
- ];
191
- const availableStyles = getAvailableOutputStyles();
192
- if (hasLegacyPersonalityFiles() && !preselectedStyles) {
193
- console.log(ansis.yellow(`\u26A0\uFE0F ${i18n.t("configuration:legacyFilesDetected")}`));
194
- const cleanupLegacy = await promptBoolean({
195
- message: i18n.t("configuration:cleanupLegacyFiles"),
196
- defaultValue: true
197
- });
198
- if (cleanupLegacy) {
199
- cleanupLegacyPersonalityFiles();
200
- console.log(ansis.green(`\u2714 ${i18n.t("configuration:legacyFilesRemoved")}`));
201
- }
202
- } else if (hasLegacyPersonalityFiles() && preselectedStyles) {
203
- cleanupLegacyPersonalityFiles();
204
- }
205
- let selectedStyles;
206
- let defaultStyle;
207
- if (preselectedStyles && preselectedDefault) {
208
- selectedStyles = preselectedStyles;
209
- defaultStyle = preselectedDefault;
210
- } else {
211
- const customStyles = availableStyles.filter((style) => style.isCustom);
212
- const { selectedStyles: promptedStyles } = await inquirer.prompt({
213
- type: "checkbox",
214
- name: "selectedStyles",
215
- message: `${i18n.t("configuration:selectOutputStyles")}${i18n.t("common:multiSelectHint")}`,
216
- choices: addNumbersToChoices(customStyles.map((style) => {
217
- const styleInfo = outputStyleList.find((s) => s.id === style.id);
218
- return {
219
- name: `${styleInfo?.name || style.id} - ${ansis.gray(styleInfo?.description || "")}`,
220
- value: style.id,
221
- checked: true
222
- // Default to all selected
223
- };
224
- })),
225
- validate: async (input) => input.length > 0 || i18n.t("configuration:selectAtLeastOne")
226
- });
227
- if (!promptedStyles || promptedStyles.length === 0) {
228
- console.log(ansis.yellow(i18n.t("common:cancelled")));
229
- return;
230
- }
231
- selectedStyles = promptedStyles;
232
- const { defaultStyle: promptedDefault } = await inquirer.prompt({
233
- type: "list",
234
- name: "defaultStyle",
235
- message: i18n.t("configuration:selectDefaultOutputStyle"),
236
- choices: addNumbersToChoices([
237
- // Show selected custom styles first (only what user actually installed)
238
- ...selectedStyles.map((styleId) => {
239
- const styleInfo = outputStyleList.find((s) => s.id === styleId);
240
- return {
241
- name: `${styleInfo?.name || styleId} - ${ansis.gray(styleInfo?.description || "")}`,
242
- value: styleId,
243
- short: styleInfo?.name || styleId
244
- };
245
- }),
246
- // Then show all built-in styles (always available)
247
- ...availableStyles.filter((style) => !style.isCustom).map((style) => {
248
- const styleInfo = outputStyleList.find((s) => s.id === style.id);
249
- return {
250
- name: `${styleInfo?.name || style.id} - ${ansis.gray(styleInfo?.description || "")}`,
251
- value: style.id,
252
- short: styleInfo?.name || style.id
253
- };
254
- })
255
- ]),
256
- default: selectedStyles.includes("linus-mode") ? "linus-mode" : selectedStyles[0]
257
- });
258
- if (!promptedDefault) {
259
- console.log(ansis.yellow(i18n.t("common:cancelled")));
260
- return;
261
- }
262
- defaultStyle = promptedDefault;
263
- }
264
- await copyOutputStyles(selectedStyles, "zh-CN");
265
- setGlobalDefaultOutputStyle(defaultStyle);
266
- updateZcfConfig({
267
- outputStyles: selectedStyles,
268
- defaultOutputStyle: defaultStyle
269
- });
270
- console.log(ansis.green(`\u2714 ${i18n.t("configuration:outputStyleInstalled")}`));
271
- console.log(ansis.gray(` ${i18n.t("configuration:selectedStyles")}: ${selectedStyles.join(", ")}`));
272
- console.log(ansis.gray(` ${i18n.t("configuration:defaultStyle")}: ${defaultStyle}`));
273
- }
274
-
275
- function validateApiKey(apiKey) {
276
- if (!apiKey || apiKey.trim() === "") {
277
- return {
278
- isValid: false,
279
- // Note: This should use i18next, but due to sync constraint in inquirer validate,
280
- // we temporarily use a generic message. This will be fixed when we refactor to async validation.
281
- error: "API key cannot be empty"
282
- };
283
- }
284
- return { isValid: true };
285
- }
286
- function formatApiKeyDisplay(apiKey) {
287
- if (!apiKey || apiKey.length < 12) {
288
- return apiKey;
289
- }
290
- return `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 4)}`;
291
- }
292
-
293
- async function configureApiCompletely(preselectedAuthType) {
294
- ensureI18nInitialized();
295
- let authType = preselectedAuthType;
296
- if (!authType) {
297
- const { authType: selectedAuthType } = await inquirer.prompt({
298
- type: "list",
299
- name: "authType",
300
- message: i18n.t("api:configureApi"),
301
- choices: addNumbersToChoices([
302
- {
303
- name: i18n.t("api:useOfficialLogin"),
304
- value: "official",
305
- short: i18n.t("api:useOfficialLogin")
306
- },
307
- {
308
- name: `${i18n.t("api:useAuthToken")} - ${ansis.gray(i18n.t("api:authTokenDesc"))}`,
309
- value: "auth_token",
310
- short: i18n.t("api:useAuthToken")
311
- },
312
- {
313
- name: `${i18n.t("api:useApiKey")} - ${ansis.gray(i18n.t("api:apiKeyDesc"))}`,
314
- value: "api_key",
315
- short: i18n.t("api:useApiKey")
316
- }
317
- ])
318
- });
319
- if (!selectedAuthType) {
320
- console.log(ansis.yellow(i18n.t("common:cancelled")));
321
- return null;
322
- }
323
- authType = selectedAuthType;
324
- }
325
- if (authType === "official") {
326
- const success = switchToOfficialLogin();
327
- if (success) {
328
- return null;
329
- } else {
330
- console.log(ansis.red(i18n.t("api:officialLoginFailed")));
331
- return null;
332
- }
333
- }
334
- const { url } = await inquirer.prompt({
335
- type: "input",
336
- name: "url",
337
- message: i18n.t("api:enterApiUrl"),
338
- validate: async (value) => {
339
- if (!value)
340
- return i18n.t("api:urlRequired");
341
- try {
342
- void new URL(value);
343
- return true;
344
- } catch {
345
- return i18n.t("api:invalidUrl");
346
- }
347
- }
348
- });
349
- if (url === void 0) {
350
- console.log(ansis.yellow(i18n.t("common:cancelled")));
351
- return null;
352
- }
353
- const keyMessage = authType === "auth_token" ? i18n.t("api:enterAuthToken") : i18n.t("api:enterApiKey");
354
- const { key } = await inquirer.prompt({
355
- type: "input",
356
- name: "key",
357
- message: keyMessage,
358
- validate: async (value) => {
359
- if (!value) {
360
- return i18n.t("api:keyRequired");
361
- }
362
- const validation = validateApiKey(value);
363
- if (!validation.isValid) {
364
- return validation.error || i18n.t("api:invalidKeyFormat");
365
- }
366
- return true;
367
- }
368
- });
369
- if (key === void 0) {
370
- console.log(ansis.yellow(i18n.t("common:cancelled")));
371
- return null;
372
- }
373
- console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
374
- return { url, key, authType };
375
- }
376
- async function modifyApiConfigPartially(existingConfig) {
377
- ensureI18nInitialized();
378
- let currentConfig = { ...existingConfig };
379
- const latestConfig = getExistingApiConfig();
380
- if (latestConfig) {
381
- currentConfig = latestConfig;
382
- }
383
- const { item } = await inquirer.prompt({
384
- type: "list",
385
- name: "item",
386
- message: i18n.t("api:selectModifyItems"),
387
- choices: addNumbersToChoices([
388
- { name: i18n.t("api:modifyApiUrl"), value: "url" },
389
- { name: i18n.t("api:modifyApiKey"), value: "key" },
390
- { name: i18n.t("api:modifyAuthType"), value: "authType" }
391
- ])
392
- });
393
- if (!item) {
394
- console.log(ansis.yellow(i18n.t("common:cancelled")));
395
- return;
396
- }
397
- if (item === "url") {
398
- const { url } = await inquirer.prompt({
399
- type: "input",
400
- name: "url",
401
- message: i18n.t("api:enterNewApiUrl").replace("{url}", currentConfig.url || i18n.t("common:none")),
402
- default: currentConfig.url,
403
- validate: async (value) => {
404
- if (!value)
405
- return i18n.t("api:urlRequired");
406
- try {
407
- void new URL(value);
408
- return true;
409
- } catch {
410
- return i18n.t("api:invalidUrl");
411
- }
412
- }
413
- });
414
- if (url === void 0) {
415
- console.log(ansis.yellow(i18n.t("common:cancelled")));
416
- return;
417
- }
418
- currentConfig.url = url;
419
- const savedConfig = configureApi(currentConfig);
420
- if (savedConfig) {
421
- console.log(ansis.green(`\u2714 ${i18n.t("api:modificationSaved")}`));
422
- console.log(ansis.gray(` ${i18n.t("api:apiConfigUrl")}: ${savedConfig.url}`));
423
- }
424
- } else if (item === "key") {
425
- const authType = currentConfig.authType || "auth_token";
426
- const keyMessage = authType === "auth_token" ? i18n.t("api:enterNewApiKey").replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.t("common:none")) : i18n.t("api:enterNewApiKey").replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.t("common:none"));
427
- const { key } = await inquirer.prompt({
428
- type: "input",
429
- name: "key",
430
- message: keyMessage,
431
- validate: async (value) => {
432
- if (!value) {
433
- return i18n.t("api:keyRequired");
434
- }
435
- const validation = validateApiKey(value);
436
- if (!validation.isValid) {
437
- return validation.error || i18n.t("api:invalidKeyFormat");
438
- }
439
- return true;
440
- }
441
- });
442
- if (key === void 0) {
443
- console.log(ansis.yellow(i18n.t("common:cancelled")));
444
- return;
445
- }
446
- currentConfig.key = key;
447
- const savedConfig = configureApi(currentConfig);
448
- if (savedConfig) {
449
- console.log(ansis.green(`\u2714 ${i18n.t("api:modificationSaved")}`));
450
- console.log(ansis.gray(` ${i18n.t("api:apiConfigKey")}: ${formatApiKeyDisplay(savedConfig.key)}`));
451
- }
452
- } else if (item === "authType") {
453
- const { authType } = await inquirer.prompt({
454
- type: "list",
455
- name: "authType",
456
- message: i18n.t("api:selectNewAuthType").replace("{type}", currentConfig.authType || i18n.t("common:none")),
457
- choices: addNumbersToChoices([
458
- { name: "Auth Token (OAuth)", value: "auth_token" },
459
- { name: "API Key", value: "api_key" }
460
- ]),
461
- default: currentConfig.authType === "api_key" ? 1 : 0
462
- });
463
- if (authType === void 0) {
464
- console.log(ansis.yellow(i18n.t("common:cancelled")));
465
- return;
466
- }
467
- currentConfig.authType = authType;
468
- const savedConfig = configureApi(currentConfig);
469
- if (savedConfig) {
470
- console.log(ansis.green(`\u2714 ${i18n.t("api:modificationSaved")}`));
471
- console.log(ansis.gray(` ${i18n.t("api:apiConfigAuthType")}: ${savedConfig.authType}`));
472
- }
473
- }
474
- }
475
- async function updatePromptOnly(aiOutputLang) {
476
- ensureI18nInitialized();
477
- const backupDir = backupExistingConfig();
478
- if (backupDir) {
479
- console.log(ansis.gray(`\u2714 ${i18n.t("configuration:backupSuccess")}: ${backupDir}`));
480
- }
481
- if (aiOutputLang) {
482
- applyAiLanguageDirective(aiOutputLang);
483
- }
484
- await configureOutputStyle();
485
- console.log(ansis.green(`\u2714 ${i18n.t("configuration:configSuccess")} ${CLAUDE_DIR}`));
486
- console.log(`
487
- ${ansis.green(i18n.t("common:complete"))}`);
488
- }
489
-
490
- export { configureOutputStyle as a, configureApiCompletely as c, formatApiKeyDisplay as f, modifyApiConfigPartially as m, updatePromptOnly as u, validateApiKey as v };
@@ -1,209 +0,0 @@
1
- import ansis from 'ansis';
2
- import inquirer from 'inquirer';
3
- import 'node:child_process';
4
- import { homedir } from 'node:os';
5
- import 'node:process';
6
- import { join } from 'pathe';
7
- import { ensureI18nInitialized, i18n } from '../chunks/index.mjs';
8
-
9
- const PLAYWRIGHT_PROFILES_DIR = join(homedir(), ".ccjk", "playwright");
10
- function createPlaywrightMcpConfig(options = {}) {
11
- const {
12
- profile = "default",
13
- headless = false,
14
- browser = "chromium",
15
- userDataDir
16
- } = options;
17
- const resolvedUserDataDir = userDataDir || join(PLAYWRIGHT_PROFILES_DIR, profile);
18
- const args = ["-y", "@playwright/mcp@latest"];
19
- args.push("--browser", browser);
20
- args.push("--user-data-dir", resolvedUserDataDir);
21
- if (headless) {
22
- args.push("--headless");
23
- }
24
- return {
25
- type: "stdio",
26
- command: "npx",
27
- args,
28
- env: {}
29
- };
30
- }
31
- const MCP_SERVICE_CONFIGS = [
32
- // Documentation and Search Services - Universal (no GUI required)
33
- {
34
- id: "context7",
35
- requiresApiKey: false,
36
- config: {
37
- type: "stdio",
38
- command: "npx",
39
- args: ["-y", "@upstash/context7-mcp@latest"],
40
- env: {}
41
- }
42
- // Works on all platforms - no special requirements
43
- },
44
- {
45
- id: "open-websearch",
46
- requiresApiKey: false,
47
- config: {
48
- type: "stdio",
49
- command: "npx",
50
- args: ["-y", "open-websearch@latest"],
51
- env: {
52
- MODE: "stdio",
53
- DEFAULT_SEARCH_ENGINE: "duckduckgo",
54
- ALLOWED_SEARCH_ENGINES: "duckduckgo,bing,brave"
55
- }
56
- }
57
- // Works on all platforms - no special requirements
58
- },
59
- {
60
- id: "mcp-deepwiki",
61
- requiresApiKey: false,
62
- config: {
63
- type: "stdio",
64
- command: "npx",
65
- args: ["-y", "mcp-deepwiki@latest"],
66
- env: {}
67
- }
68
- // Works on all platforms - no special requirements
69
- },
70
- // Development Workflow Services
71
- {
72
- id: "spec-workflow",
73
- requiresApiKey: false,
74
- config: {
75
- type: "stdio",
76
- command: "npx",
77
- args: ["-y", "@pimzino/spec-workflow-mcp@latest"],
78
- env: {}
79
- }
80
- // Works on all platforms - no special requirements
81
- },
82
- {
83
- id: "serena",
84
- requiresApiKey: false,
85
- config: {
86
- type: "stdio",
87
- command: "uvx",
88
- args: ["--from", "git+https://github.com/oraios/serena", "serena", "start-mcp-server", "--context", "ide-assistant", "--enable-web-dashboard", "false"],
89
- env: {}
90
- },
91
- platformRequirements: {
92
- requiredCommands: ["uvx"]
93
- // Requires uv/uvx to be installed
94
- }
95
- },
96
- // Browser and Automation Services - Require GUI environment
97
- {
98
- id: "Playwright",
99
- requiresApiKey: false,
100
- config: createPlaywrightMcpConfig(),
101
- // Uses default profile with chromium browser
102
- platformRequirements: {
103
- platforms: ["macos", "windows"],
104
- // GUI required - exclude headless Linux/WSL/Termux
105
- requiresGui: true
106
- }
107
- },
108
- // Anthropic Official MCP Services - Universal
109
- // Note: Removed low-value services: filesystem (buggy), puppeteer (duplicate of Playwright),
110
- // memory (Claude has built-in memory), fetch (Claude has WebFetch), sequential-thinking (limited value)
111
- {
112
- id: "sqlite",
113
- requiresApiKey: false,
114
- config: {
115
- type: "stdio",
116
- command: "npx",
117
- args: ["-y", "@anthropic-ai/mcp-server-sqlite@latest"],
118
- env: {}
119
- }
120
- // Works on all platforms - no special requirements
121
- }
122
- ];
123
- async function getMcpServices() {
124
- ensureI18nInitialized();
125
- const mcpServiceList = [
126
- // Documentation and Search Services
127
- {
128
- id: "context7",
129
- name: i18n.t("mcp:services.context7.name"),
130
- description: i18n.t("mcp:services.context7.description")
131
- },
132
- {
133
- id: "open-websearch",
134
- name: i18n.t("mcp:services.open-websearch.name"),
135
- description: i18n.t("mcp:services.open-websearch.description")
136
- },
137
- {
138
- id: "mcp-deepwiki",
139
- name: i18n.t("mcp:services.mcp-deepwiki.name"),
140
- description: i18n.t("mcp:services.mcp-deepwiki.description")
141
- },
142
- // Development Workflow Services
143
- {
144
- id: "spec-workflow",
145
- name: i18n.t("mcp:services.spec-workflow.name"),
146
- description: i18n.t("mcp:services.spec-workflow.description")
147
- },
148
- {
149
- id: "serena",
150
- name: i18n.t("mcp:services.serena.name"),
151
- description: i18n.t("mcp:services.serena.description")
152
- },
153
- // Browser and Automation Services
154
- {
155
- id: "Playwright",
156
- name: i18n.t("mcp:services.playwright.name"),
157
- description: i18n.t("mcp:services.playwright.description")
158
- },
159
- // Anthropic Official MCP Services
160
- // Note: Removed low-value services: filesystem (buggy), puppeteer (duplicate),
161
- // memory (Claude built-in), fetch (Claude WebFetch), sequential-thinking (limited value)
162
- {
163
- id: "sqlite",
164
- name: i18n.t("mcp:services.sqlite.name"),
165
- description: i18n.t("mcp:services.sqlite.description")
166
- }
167
- ];
168
- return MCP_SERVICE_CONFIGS.map((config) => {
169
- const serviceInfo = mcpServiceList.find((s) => s.id === config.id);
170
- const service = {
171
- id: config.id,
172
- name: serviceInfo?.name || config.id,
173
- description: serviceInfo?.description || "",
174
- requiresApiKey: config.requiresApiKey,
175
- config: config.config
176
- };
177
- if (config.apiKeyEnvVar) {
178
- service.apiKeyEnvVar = config.apiKeyEnvVar;
179
- }
180
- return service;
181
- });
182
- }
183
- async function getMcpService(id) {
184
- const services = await getMcpServices();
185
- return services.find((service) => service.id === id);
186
- }
187
-
188
- async function selectMcpServices() {
189
- ensureI18nInitialized();
190
- const mcpServices = await getMcpServices();
191
- const choices = mcpServices.map((service) => ({
192
- name: `${service.name} - ${ansis.gray(service.description)}`,
193
- value: service.id,
194
- selected: false
195
- }));
196
- const { services } = await inquirer.prompt({
197
- type: "checkbox",
198
- name: "services",
199
- message: `${i18n.t("mcp:selectMcpServices")}${i18n.t("common:multiSelectHint")}`,
200
- choices
201
- });
202
- if (services === void 0) {
203
- console.log(ansis.yellow(i18n.t("common:cancelled")));
204
- return void 0;
205
- }
206
- return services;
207
- }
208
-
209
- export { MCP_SERVICE_CONFIGS as M, getMcpServices as a, getMcpService as g, selectMcpServices as s };