zcf 2.9.11 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/chunks/simple-config.mjs +1464 -1246
- package/dist/cli.mjs +31 -3
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.mjs +6 -6
- package/package.json +1 -1
|
@@ -1,23 +1,83 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
1
|
import ansis from 'ansis';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
3
|
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
4
4
|
import { join, dirname } from 'pathe';
|
|
5
5
|
import dayjs from 'dayjs';
|
|
6
|
+
import { exec as exec$1 } from 'node:child_process';
|
|
7
|
+
import { homedir, platform } from 'node:os';
|
|
8
|
+
import { join as join$1 } from 'node:path';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
6
10
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { exec } from '
|
|
8
|
-
import {
|
|
9
|
-
import { promisify } from 'util';
|
|
11
|
+
import { exec as exec$2 } from 'child_process';
|
|
12
|
+
import { promisify as promisify$1 } from 'util';
|
|
10
13
|
import ora from 'ora';
|
|
11
14
|
import semver from 'semver';
|
|
15
|
+
import { exec } from 'tinyexec';
|
|
12
16
|
import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
13
|
-
import { exec as exec$2 } from 'node:child_process';
|
|
14
|
-
import { promisify as promisify$1 } from 'node:util';
|
|
15
|
-
import { homedir, platform } from 'node:os';
|
|
16
|
-
import { join as join$1 } from 'node:path';
|
|
17
17
|
|
|
18
|
-
const version = "2.
|
|
18
|
+
const version = "2.10.1";
|
|
19
19
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
20
20
|
|
|
21
|
+
const WORKFLOW_CONFIGS = [
|
|
22
|
+
{
|
|
23
|
+
id: "sixStepsWorkflow",
|
|
24
|
+
nameKey: "workflowOption.sixStepsWorkflow",
|
|
25
|
+
descriptionKey: "workflowDescription.sixStepsWorkflow",
|
|
26
|
+
defaultSelected: true,
|
|
27
|
+
order: 1,
|
|
28
|
+
commands: ["workflow.md"],
|
|
29
|
+
agents: [],
|
|
30
|
+
autoInstallAgents: false,
|
|
31
|
+
category: "sixStep",
|
|
32
|
+
outputDir: "workflow"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "featPlanUx",
|
|
36
|
+
nameKey: "workflowOption.featPlanUx",
|
|
37
|
+
descriptionKey: "workflowDescription.featPlanUx",
|
|
38
|
+
defaultSelected: true,
|
|
39
|
+
order: 2,
|
|
40
|
+
commands: ["feat.md"],
|
|
41
|
+
agents: [
|
|
42
|
+
{ id: "planner", filename: "planner.md", required: true },
|
|
43
|
+
{ id: "ui-ux-designer", filename: "ui-ux-designer.md", required: true }
|
|
44
|
+
],
|
|
45
|
+
autoInstallAgents: true,
|
|
46
|
+
category: "plan",
|
|
47
|
+
outputDir: "feat"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "gitWorkflow",
|
|
51
|
+
nameKey: "workflowOption.gitWorkflow",
|
|
52
|
+
descriptionKey: "workflowDescription.gitWorkflow",
|
|
53
|
+
defaultSelected: true,
|
|
54
|
+
order: 3,
|
|
55
|
+
commands: ["git-commit.md", "git-rollback.md", "git-cleanBranches.md", "git-worktree.md"],
|
|
56
|
+
agents: [],
|
|
57
|
+
autoInstallAgents: false,
|
|
58
|
+
category: "git",
|
|
59
|
+
outputDir: "git"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "bmadWorkflow",
|
|
63
|
+
nameKey: "workflowOption.bmadWorkflow",
|
|
64
|
+
descriptionKey: "workflowDescription.bmadWorkflow",
|
|
65
|
+
defaultSelected: true,
|
|
66
|
+
order: 4,
|
|
67
|
+
commands: ["bmad-init.md"],
|
|
68
|
+
agents: [],
|
|
69
|
+
autoInstallAgents: false,
|
|
70
|
+
category: "bmad",
|
|
71
|
+
outputDir: "bmad"
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
function getWorkflowConfig(workflowId) {
|
|
75
|
+
return WORKFLOW_CONFIGS.find((config) => config.id === workflowId);
|
|
76
|
+
}
|
|
77
|
+
function getOrderedWorkflows() {
|
|
78
|
+
return [...WORKFLOW_CONFIGS].sort((a, b) => a.order - b.order);
|
|
79
|
+
}
|
|
80
|
+
|
|
21
81
|
const common$1 = {
|
|
22
82
|
// Basic
|
|
23
83
|
multiSelectHint: "\uFF08\u7A7A\u683C\u9009\u62E9\uFF0Ca\u5168\u9009\uFF0Ci\u53CD\u9009\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
|
|
@@ -1129,21 +1189,6 @@ const MCP_SERVICES = [
|
|
|
1129
1189
|
}
|
|
1130
1190
|
];
|
|
1131
1191
|
|
|
1132
|
-
function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
|
|
1133
|
-
let currentNumber = startFrom;
|
|
1134
|
-
return choices.map((choice) => {
|
|
1135
|
-
if (choice.disabled) {
|
|
1136
|
-
return choice;
|
|
1137
|
-
}
|
|
1138
|
-
const numbered = {
|
|
1139
|
-
...choice,
|
|
1140
|
-
name: `${format(currentNumber)}${choice.name}`
|
|
1141
|
-
};
|
|
1142
|
-
currentNumber++;
|
|
1143
|
-
return numbered;
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
1192
|
class FileSystemError extends Error {
|
|
1148
1193
|
constructor(message, path, cause) {
|
|
1149
1194
|
super(message);
|
|
@@ -1324,6 +1369,21 @@ function updateZcfConfig(updates) {
|
|
|
1324
1369
|
writeZcfConfig(newConfig);
|
|
1325
1370
|
}
|
|
1326
1371
|
|
|
1372
|
+
function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
|
|
1373
|
+
let currentNumber = startFrom;
|
|
1374
|
+
return choices.map((choice) => {
|
|
1375
|
+
if (choice.disabled) {
|
|
1376
|
+
return choice;
|
|
1377
|
+
}
|
|
1378
|
+
const numbered = {
|
|
1379
|
+
...choice,
|
|
1380
|
+
name: `${format(currentNumber)}${choice.name}`
|
|
1381
|
+
};
|
|
1382
|
+
currentNumber++;
|
|
1383
|
+
return numbered;
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1327
1387
|
const AI_PERSONALITIES = [
|
|
1328
1388
|
{
|
|
1329
1389
|
id: "professional",
|
|
@@ -1370,9 +1430,29 @@ function getExistingPersonality() {
|
|
|
1370
1430
|
function getPersonalityInfo(personalityId) {
|
|
1371
1431
|
return AI_PERSONALITIES.find((p) => p.id === personalityId);
|
|
1372
1432
|
}
|
|
1373
|
-
async function configureAiPersonality(scriptLang,
|
|
1433
|
+
async function configureAiPersonality(scriptLang, preselectedPersonality) {
|
|
1434
|
+
const showExisting = typeof preselectedPersonality === "boolean" ? preselectedPersonality : true;
|
|
1435
|
+
const preselected = typeof preselectedPersonality === "string" ? preselectedPersonality : void 0;
|
|
1374
1436
|
const i18n = getTranslation(scriptLang);
|
|
1375
1437
|
const existingPersonality = getExistingPersonality();
|
|
1438
|
+
if (preselected) {
|
|
1439
|
+
let directive2 = "";
|
|
1440
|
+
if (preselected === "custom") {
|
|
1441
|
+
directive2 = "You are a helpful assistant.";
|
|
1442
|
+
} else {
|
|
1443
|
+
const selected = AI_PERSONALITIES.find((p) => p.id === preselected);
|
|
1444
|
+
if (selected) {
|
|
1445
|
+
directive2 = selected.directive[scriptLang];
|
|
1446
|
+
} else {
|
|
1447
|
+
console.error(ansis.red(`Invalid personality: ${preselected}`));
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
await applyPersonalityDirective(directive2);
|
|
1452
|
+
updateZcfConfig({ aiPersonality: preselected });
|
|
1453
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1376
1456
|
if (showExisting && existingPersonality) {
|
|
1377
1457
|
const personalityInfo = getPersonalityInfo(existingPersonality);
|
|
1378
1458
|
if (personalityInfo) {
|
|
@@ -1469,30 +1549,6 @@ function displayBannerWithInfo(subtitle) {
|
|
|
1469
1549
|
`));
|
|
1470
1550
|
}
|
|
1471
1551
|
|
|
1472
|
-
function handleExitPromptError(error) {
|
|
1473
|
-
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
1474
|
-
const zcfConfig = readZcfConfig();
|
|
1475
|
-
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
1476
|
-
const i18n = getTranslation(defaultLang);
|
|
1477
|
-
console.log(ansis.cyan(`
|
|
1478
|
-
${i18n.common.goodbye}
|
|
1479
|
-
`));
|
|
1480
|
-
process.exit(0);
|
|
1481
|
-
}
|
|
1482
|
-
return false;
|
|
1483
|
-
}
|
|
1484
|
-
function handleGeneralError(error, lang) {
|
|
1485
|
-
const zcfConfig = readZcfConfig();
|
|
1486
|
-
const defaultLang = lang || zcfConfig?.preferredLang || "en";
|
|
1487
|
-
const i18n = getTranslation(defaultLang);
|
|
1488
|
-
const errorMsg = i18n.common.error || "Error";
|
|
1489
|
-
console.error(ansis.red(`${errorMsg}:`), error);
|
|
1490
|
-
if (error instanceof Error) {
|
|
1491
|
-
console.error(ansis.gray(`Stack: ${error.stack}`));
|
|
1492
|
-
}
|
|
1493
|
-
process.exit(1);
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
1552
|
function mergeArraysUnique(arr1, arr2) {
|
|
1497
1553
|
const combined = [...arr1 || [], ...arr2 || []];
|
|
1498
1554
|
return [...new Set(combined)];
|
|
@@ -1927,247 +1983,470 @@ function applyAiLanguageDirective(aiOutputLang) {
|
|
|
1927
1983
|
writeFile(languageFile, directive);
|
|
1928
1984
|
}
|
|
1929
1985
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
return { isValid: true };
|
|
1939
|
-
}
|
|
1940
|
-
function formatApiKeyDisplay(apiKey) {
|
|
1941
|
-
if (!apiKey || apiKey.length < 12) {
|
|
1942
|
-
return apiKey;
|
|
1943
|
-
}
|
|
1944
|
-
return `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 4)}`;
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
1948
|
-
let authType = preselectedAuthType;
|
|
1949
|
-
if (!authType) {
|
|
1950
|
-
const { authType: selectedAuthType } = await inquirer.prompt({
|
|
1951
|
-
type: "list",
|
|
1952
|
-
name: "authType",
|
|
1953
|
-
message: i18n.api.configureApi,
|
|
1954
|
-
choices: addNumbersToChoices([
|
|
1955
|
-
{
|
|
1956
|
-
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
1957
|
-
value: "auth_token",
|
|
1958
|
-
short: i18n.api.useAuthToken
|
|
1959
|
-
},
|
|
1960
|
-
{
|
|
1961
|
-
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
1962
|
-
value: "api_key",
|
|
1963
|
-
short: i18n.api.useApiKey
|
|
1964
|
-
}
|
|
1965
|
-
])
|
|
1986
|
+
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
1987
|
+
async function fetchProviderPresets() {
|
|
1988
|
+
try {
|
|
1989
|
+
const controller = new AbortController();
|
|
1990
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1991
|
+
const response = await fetch(PROVIDER_PRESETS_URL, {
|
|
1992
|
+
signal: controller.signal
|
|
1966
1993
|
});
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
}
|
|
1971
|
-
authType = selectedAuthType;
|
|
1972
|
-
}
|
|
1973
|
-
const { url } = await inquirer.prompt({
|
|
1974
|
-
type: "input",
|
|
1975
|
-
name: "url",
|
|
1976
|
-
message: i18n.api.enterApiUrl,
|
|
1977
|
-
validate: (value) => {
|
|
1978
|
-
if (!value) return i18n.api.urlRequired;
|
|
1979
|
-
try {
|
|
1980
|
-
new URL(value);
|
|
1981
|
-
return true;
|
|
1982
|
-
} catch {
|
|
1983
|
-
return i18n.api.invalidUrl;
|
|
1984
|
-
}
|
|
1994
|
+
clearTimeout(timeoutId);
|
|
1995
|
+
if (!response.ok) {
|
|
1996
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1985
1997
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1998
|
+
const data = await response.json();
|
|
1999
|
+
const presets = [];
|
|
2000
|
+
if (Array.isArray(data)) {
|
|
2001
|
+
for (const provider of data) {
|
|
2002
|
+
if (provider && typeof provider === "object") {
|
|
2003
|
+
presets.push({
|
|
2004
|
+
name: provider.name || "",
|
|
2005
|
+
provider: provider.name || "",
|
|
2006
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2007
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2008
|
+
models: provider.models || [],
|
|
2009
|
+
description: provider.description || provider.name || "",
|
|
2010
|
+
transformer: provider.transformer
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
1999
2013
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2014
|
+
} else if (data && typeof data === "object") {
|
|
2015
|
+
for (const [key, value] of Object.entries(data)) {
|
|
2016
|
+
if (typeof value === "object" && value !== null) {
|
|
2017
|
+
const provider = value;
|
|
2018
|
+
presets.push({
|
|
2019
|
+
name: provider.name || key,
|
|
2020
|
+
provider: key,
|
|
2021
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2022
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2023
|
+
models: provider.models || [],
|
|
2024
|
+
description: provider.description || "",
|
|
2025
|
+
transformer: provider.transformer
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2003
2028
|
}
|
|
2004
|
-
return true;
|
|
2005
2029
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
return null;
|
|
2030
|
+
return presets;
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
return getFallbackPresets();
|
|
2010
2033
|
}
|
|
2011
|
-
console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
|
|
2012
|
-
return { url, key, authType };
|
|
2013
2034
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
2028
|
-
])
|
|
2029
|
-
});
|
|
2030
|
-
if (!item) {
|
|
2031
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2032
|
-
return;
|
|
2033
|
-
}
|
|
2034
|
-
if (item === "url") {
|
|
2035
|
-
const { url } = await inquirer.prompt({
|
|
2036
|
-
type: "input",
|
|
2037
|
-
name: "url",
|
|
2038
|
-
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
2039
|
-
default: currentConfig.url,
|
|
2040
|
-
validate: (value) => {
|
|
2041
|
-
if (!value) return i18n.api.urlRequired;
|
|
2042
|
-
try {
|
|
2043
|
-
new URL(value);
|
|
2044
|
-
return true;
|
|
2045
|
-
} catch {
|
|
2046
|
-
return i18n.api.invalidUrl;
|
|
2035
|
+
function getFallbackPresets() {
|
|
2036
|
+
return [
|
|
2037
|
+
{
|
|
2038
|
+
name: "dashscope",
|
|
2039
|
+
provider: "dashscope",
|
|
2040
|
+
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
2041
|
+
requiresApiKey: true,
|
|
2042
|
+
models: ["qwen3-coder-plus"],
|
|
2043
|
+
description: "Alibaba DashScope",
|
|
2044
|
+
transformer: {
|
|
2045
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2046
|
+
"qwen3-coder-plus": {
|
|
2047
|
+
use: ["enhancetool"]
|
|
2047
2048
|
}
|
|
2048
2049
|
}
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
const authType = currentConfig.authType || "auth_token";
|
|
2062
|
-
const keyMessage = authType === "auth_token" ? i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none) : i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none);
|
|
2063
|
-
const { key } = await inquirer.prompt({
|
|
2064
|
-
type: "input",
|
|
2065
|
-
name: "key",
|
|
2066
|
-
message: keyMessage,
|
|
2067
|
-
validate: (value) => {
|
|
2068
|
-
if (!value) {
|
|
2069
|
-
return i18n.api.keyRequired;
|
|
2050
|
+
},
|
|
2051
|
+
{
|
|
2052
|
+
name: "deepseek",
|
|
2053
|
+
provider: "deepseek",
|
|
2054
|
+
baseURL: "https://api.deepseek.com/chat/completions",
|
|
2055
|
+
requiresApiKey: true,
|
|
2056
|
+
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
2057
|
+
description: "DeepSeek AI models",
|
|
2058
|
+
transformer: {
|
|
2059
|
+
use: ["deepseek"],
|
|
2060
|
+
"deepseek-chat": {
|
|
2061
|
+
use: ["tooluse"]
|
|
2070
2062
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
name: "gemini",
|
|
2067
|
+
provider: "gemini",
|
|
2068
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
2069
|
+
requiresApiKey: true,
|
|
2070
|
+
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
2071
|
+
description: "Google Gemini models",
|
|
2072
|
+
transformer: {
|
|
2073
|
+
use: ["gemini"]
|
|
2074
|
+
}
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
name: "modelscope",
|
|
2078
|
+
provider: "modelscope",
|
|
2079
|
+
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
2080
|
+
requiresApiKey: true,
|
|
2081
|
+
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
2082
|
+
description: "ModelScope AI models",
|
|
2083
|
+
transformer: {
|
|
2084
|
+
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2085
|
+
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
2086
|
+
use: ["enhancetool"]
|
|
2087
|
+
},
|
|
2088
|
+
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
2089
|
+
use: ["reasoning"]
|
|
2074
2090
|
}
|
|
2075
|
-
return true;
|
|
2076
2091
|
}
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2092
|
+
},
|
|
2093
|
+
{
|
|
2094
|
+
name: "openrouter",
|
|
2095
|
+
provider: "openrouter",
|
|
2096
|
+
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
2097
|
+
requiresApiKey: true,
|
|
2098
|
+
models: [
|
|
2099
|
+
"google/gemini-2.5-pro-preview",
|
|
2100
|
+
"anthropic/claude-sonnet-4",
|
|
2101
|
+
"anthropic/claude-3.5-sonnet",
|
|
2102
|
+
"anthropic/claude-3.7-sonnet:thinking"
|
|
2103
|
+
],
|
|
2104
|
+
description: "OpenRouter API",
|
|
2105
|
+
transformer: {
|
|
2106
|
+
use: ["openrouter"]
|
|
2107
|
+
}
|
|
2108
|
+
},
|
|
2109
|
+
{
|
|
2110
|
+
name: "siliconflow",
|
|
2111
|
+
provider: "siliconflow",
|
|
2112
|
+
baseURL: "https://api.siliconflow.cn/v1/chat/completions",
|
|
2113
|
+
requiresApiKey: true,
|
|
2114
|
+
models: ["moonshotai/Kimi-K2-Instruct"],
|
|
2115
|
+
description: "SiliconFlow AI",
|
|
2116
|
+
transformer: {
|
|
2117
|
+
use: [["maxtoken", { max_tokens: 16384 }]]
|
|
2118
|
+
}
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
name: "volcengine",
|
|
2122
|
+
provider: "volcengine",
|
|
2123
|
+
baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
2124
|
+
requiresApiKey: true,
|
|
2125
|
+
models: ["deepseek-v3-250324", "deepseek-r1-250528"],
|
|
2126
|
+
description: "Volcengine AI",
|
|
2127
|
+
transformer: {
|
|
2128
|
+
use: ["deepseek"]
|
|
2129
|
+
}
|
|
2108
2130
|
}
|
|
2109
|
-
|
|
2110
|
-
}
|
|
2111
|
-
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
2112
|
-
const i18n = getTranslation(scriptLang);
|
|
2113
|
-
const backupDir = backupExistingConfig();
|
|
2114
|
-
if (backupDir) {
|
|
2115
|
-
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2116
|
-
}
|
|
2117
|
-
copyConfigFiles(configLang, true);
|
|
2118
|
-
if (aiOutputLang) {
|
|
2119
|
-
applyAiLanguageDirective(aiOutputLang);
|
|
2120
|
-
}
|
|
2121
|
-
await configureAiPersonality(scriptLang);
|
|
2122
|
-
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
2123
|
-
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
function format(template, replacements) {
|
|
2127
|
-
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
2128
|
-
return replacements[key] || match;
|
|
2129
|
-
});
|
|
2131
|
+
];
|
|
2130
2132
|
}
|
|
2131
2133
|
|
|
2132
2134
|
const execAsync$4 = promisify(exec$1);
|
|
2133
|
-
|
|
2135
|
+
const CCR_CONFIG_DIR = join$1(homedir(), ".claude-code-router");
|
|
2136
|
+
const CCR_CONFIG_FILE = join$1(CCR_CONFIG_DIR, "config.json");
|
|
2137
|
+
const CCR_BACKUP_DIR = CCR_CONFIG_DIR;
|
|
2138
|
+
function ensureCcrConfigDir() {
|
|
2139
|
+
if (!existsSync(CCR_CONFIG_DIR)) {
|
|
2140
|
+
mkdirSync(CCR_CONFIG_DIR, { recursive: true });
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
function backupCcrConfig(scriptLang) {
|
|
2144
|
+
const i18n = getTranslation(scriptLang);
|
|
2134
2145
|
try {
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
const result = await execAsync$4(`${command} -v`);
|
|
2138
|
-
stdout = result.stdout;
|
|
2139
|
-
} catch {
|
|
2140
|
-
const result = await execAsync$4(`${command} --version`);
|
|
2141
|
-
stdout = result.stdout;
|
|
2146
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2147
|
+
return null;
|
|
2142
2148
|
}
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
|
|
2149
|
+
const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
|
|
2150
|
+
const backupFileName = `config.json.${timestamp}.bak`;
|
|
2151
|
+
const backupPath = join$1(CCR_BACKUP_DIR, backupFileName);
|
|
2152
|
+
console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
|
|
2153
|
+
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
2154
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
|
|
2155
|
+
return backupPath;
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
console.error(ansis.red(`${i18n.ccr.ccrBackupFailed}:`), error.message);
|
|
2146
2158
|
return null;
|
|
2147
2159
|
}
|
|
2148
2160
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
const { stdout } = await execAsync$4(`npm view ${packageName} version`);
|
|
2152
|
-
return stdout.trim();
|
|
2153
|
-
} catch {
|
|
2161
|
+
function readCcrConfig() {
|
|
2162
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
2154
2163
|
return null;
|
|
2155
2164
|
}
|
|
2165
|
+
return readJsonConfig(CCR_CONFIG_FILE);
|
|
2156
2166
|
}
|
|
2157
|
-
function
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
}
|
|
2161
|
-
return semver.compare(current, latest);
|
|
2167
|
+
function writeCcrConfig(config) {
|
|
2168
|
+
ensureCcrConfigDir();
|
|
2169
|
+
writeJsonConfig(CCR_CONFIG_FILE, config);
|
|
2162
2170
|
}
|
|
2163
|
-
function
|
|
2164
|
-
|
|
2171
|
+
async function configureCcrProxy(ccrConfig) {
|
|
2172
|
+
const settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
2173
|
+
const host = ccrConfig.HOST || "127.0.0.1";
|
|
2174
|
+
const port = ccrConfig.PORT || 3456;
|
|
2175
|
+
const apiKey = ccrConfig.APIKEY || "sk-zcf-x-ccr";
|
|
2176
|
+
if (!settings.env) {
|
|
2177
|
+
settings.env = {};
|
|
2178
|
+
}
|
|
2179
|
+
settings.env.ANTHROPIC_BASE_URL = `http://${host}:${port}`;
|
|
2180
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
2181
|
+
writeJsonConfig(SETTINGS_FILE, settings);
|
|
2165
2182
|
}
|
|
2166
|
-
async function
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2183
|
+
async function selectCcrPreset(scriptLang) {
|
|
2184
|
+
const i18n = getTranslation(scriptLang);
|
|
2185
|
+
console.log(ansis.cyan(`${i18n.ccr.fetchingPresets}`));
|
|
2186
|
+
const presets = await fetchProviderPresets();
|
|
2187
|
+
if (!presets || presets.length === 0) {
|
|
2188
|
+
console.log(ansis.yellow(`${i18n.ccr.noPresetsAvailable}`));
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2191
|
+
try {
|
|
2192
|
+
const choices = [
|
|
2193
|
+
{
|
|
2194
|
+
name: `1. ${i18n.ccr.skipOption}`,
|
|
2195
|
+
value: "skip"
|
|
2196
|
+
},
|
|
2197
|
+
...presets.map((p, index) => ({
|
|
2198
|
+
name: `${index + 2}. ${p.name}`,
|
|
2199
|
+
value: p
|
|
2200
|
+
}))
|
|
2201
|
+
];
|
|
2202
|
+
const { preset } = await inquirer.prompt({
|
|
2203
|
+
type: "list",
|
|
2204
|
+
name: "preset",
|
|
2205
|
+
message: i18n.ccr.selectCcrPreset,
|
|
2206
|
+
choices
|
|
2207
|
+
});
|
|
2208
|
+
return preset;
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
if (error.name === "ExitPromptError") {
|
|
2211
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
throw error;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
async function configureCcrWithPreset(preset, scriptLang) {
|
|
2218
|
+
const i18n = getTranslation(scriptLang);
|
|
2219
|
+
const provider = {
|
|
2220
|
+
name: preset.name,
|
|
2221
|
+
// Use the original name from JSON
|
|
2222
|
+
api_base_url: preset.baseURL || "",
|
|
2223
|
+
api_key: "",
|
|
2224
|
+
models: preset.models
|
|
2225
|
+
};
|
|
2226
|
+
if (preset.transformer) {
|
|
2227
|
+
provider.transformer = preset.transformer;
|
|
2228
|
+
}
|
|
2229
|
+
if (preset.requiresApiKey) {
|
|
2230
|
+
try {
|
|
2231
|
+
const { apiKey } = await inquirer.prompt({
|
|
2232
|
+
type: "input",
|
|
2233
|
+
name: "apiKey",
|
|
2234
|
+
message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
|
|
2235
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
2236
|
+
});
|
|
2237
|
+
provider.api_key = apiKey;
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
if (error.name === "ExitPromptError") {
|
|
2240
|
+
throw error;
|
|
2241
|
+
}
|
|
2242
|
+
throw error;
|
|
2243
|
+
}
|
|
2244
|
+
} else {
|
|
2245
|
+
provider.api_key = "sk-free";
|
|
2246
|
+
}
|
|
2247
|
+
let defaultModel = preset.models[0];
|
|
2248
|
+
if (preset.models.length > 1) {
|
|
2249
|
+
try {
|
|
2250
|
+
const { model } = await inquirer.prompt({
|
|
2251
|
+
type: "list",
|
|
2252
|
+
name: "model",
|
|
2253
|
+
message: i18n.ccr.selectDefaultModelForProvider.replace("{provider}", preset.name),
|
|
2254
|
+
choices: preset.models.map((m, index) => ({
|
|
2255
|
+
name: `${index + 1}. ${m}`,
|
|
2256
|
+
value: m
|
|
2257
|
+
}))
|
|
2258
|
+
});
|
|
2259
|
+
defaultModel = model;
|
|
2260
|
+
} catch (error) {
|
|
2261
|
+
if (error.name === "ExitPromptError") {
|
|
2262
|
+
throw error;
|
|
2263
|
+
}
|
|
2264
|
+
throw error;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
const router = {
|
|
2268
|
+
default: `${preset.name},${defaultModel}`,
|
|
2269
|
+
// Use the original name
|
|
2270
|
+
background: `${preset.name},${defaultModel}`,
|
|
2271
|
+
think: `${preset.name},${defaultModel}`,
|
|
2272
|
+
longContext: `${preset.name},${defaultModel}`,
|
|
2273
|
+
longContextThreshold: 6e4,
|
|
2274
|
+
webSearch: `${preset.name},${defaultModel}`
|
|
2275
|
+
};
|
|
2276
|
+
const config = {
|
|
2277
|
+
LOG: true,
|
|
2278
|
+
CLAUDE_PATH: "",
|
|
2279
|
+
HOST: "127.0.0.1",
|
|
2280
|
+
PORT: 3456,
|
|
2281
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2282
|
+
API_TIMEOUT_MS: "600000",
|
|
2283
|
+
PROXY_URL: "",
|
|
2284
|
+
transformers: [],
|
|
2285
|
+
Providers: [provider],
|
|
2286
|
+
Router: router
|
|
2287
|
+
};
|
|
2288
|
+
return config;
|
|
2289
|
+
}
|
|
2290
|
+
async function restartAndCheckCcrStatus(scriptLang) {
|
|
2291
|
+
const i18n = getTranslation(scriptLang);
|
|
2292
|
+
try {
|
|
2293
|
+
console.log(ansis.cyan(`${i18n.ccr.restartingCcr}`));
|
|
2294
|
+
await execAsync$4("ccr restart");
|
|
2295
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestartSuccess}`));
|
|
2296
|
+
console.log(ansis.cyan(`${i18n.ccr.checkingCcrStatus}`));
|
|
2297
|
+
const { stdout } = await execAsync$4("ccr status");
|
|
2298
|
+
console.log(ansis.gray(stdout));
|
|
2299
|
+
} catch (error) {
|
|
2300
|
+
console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
|
|
2301
|
+
if (process.env.DEBUG) {
|
|
2302
|
+
console.error("Full error:", error);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
function showConfigurationTips(scriptLang, apiKey) {
|
|
2307
|
+
const i18n = getTranslation(scriptLang);
|
|
2308
|
+
console.log(ansis.bold.cyan(`
|
|
2309
|
+
\u{1F4CC} ${i18n.ccr.configTips}:`));
|
|
2310
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.advancedConfigTip}`));
|
|
2311
|
+
console.log(ansis.blue(` \u2022 ${i18n.ccr.manualConfigTip}`));
|
|
2312
|
+
console.log(ansis.bold.yellow(` \u2022 ${i18n.ccr.useClaudeCommand}`));
|
|
2313
|
+
if (apiKey) {
|
|
2314
|
+
console.log(ansis.bold.green(` \u2022 ${i18n.ccr.ccrUiApiKey || "CCR UI API Key"}: ${apiKey}`));
|
|
2315
|
+
console.log(ansis.gray(` ${i18n.ccr.ccrUiApiKeyHint || "Use this API key to login to CCR UI"}`));
|
|
2316
|
+
}
|
|
2317
|
+
console.log("");
|
|
2318
|
+
}
|
|
2319
|
+
function createDefaultCcrConfig() {
|
|
2320
|
+
return {
|
|
2321
|
+
LOG: false,
|
|
2322
|
+
CLAUDE_PATH: "",
|
|
2323
|
+
HOST: "127.0.0.1",
|
|
2324
|
+
PORT: 3456,
|
|
2325
|
+
APIKEY: "sk-zcf-x-ccr",
|
|
2326
|
+
API_TIMEOUT_MS: "600000",
|
|
2327
|
+
PROXY_URL: "",
|
|
2328
|
+
transformers: [],
|
|
2329
|
+
Providers: [],
|
|
2330
|
+
// Empty providers array - user configures in UI
|
|
2331
|
+
Router: {}
|
|
2332
|
+
// Empty router configuration - user configures in UI
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
async function setupCcrConfiguration(scriptLang) {
|
|
2336
|
+
const i18n = getTranslation(scriptLang);
|
|
2337
|
+
try {
|
|
2338
|
+
const existingConfig = readCcrConfig();
|
|
2339
|
+
if (existingConfig) {
|
|
2340
|
+
console.log(ansis.blue(`\u2139 ${i18n.ccr.existingCcrConfig}`));
|
|
2341
|
+
let shouldBackupAndReconfigure = false;
|
|
2342
|
+
try {
|
|
2343
|
+
const result = await inquirer.prompt({
|
|
2344
|
+
type: "confirm",
|
|
2345
|
+
name: "overwrite",
|
|
2346
|
+
message: i18n.ccr.overwriteCcrConfig,
|
|
2347
|
+
default: false
|
|
2348
|
+
});
|
|
2349
|
+
shouldBackupAndReconfigure = result.overwrite;
|
|
2350
|
+
} catch (error) {
|
|
2351
|
+
if (error.name === "ExitPromptError") {
|
|
2352
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2353
|
+
return false;
|
|
2354
|
+
}
|
|
2355
|
+
throw error;
|
|
2356
|
+
}
|
|
2357
|
+
if (!shouldBackupAndReconfigure) {
|
|
2358
|
+
console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
|
|
2359
|
+
await configureCcrProxy(existingConfig);
|
|
2360
|
+
return true;
|
|
2361
|
+
}
|
|
2362
|
+
backupCcrConfig(scriptLang);
|
|
2363
|
+
}
|
|
2364
|
+
const preset = await selectCcrPreset(scriptLang);
|
|
2365
|
+
if (!preset) {
|
|
2366
|
+
return false;
|
|
2367
|
+
}
|
|
2368
|
+
let config;
|
|
2369
|
+
if (preset === "skip") {
|
|
2370
|
+
console.log(ansis.yellow(`${i18n.ccr.skipConfiguring}`));
|
|
2371
|
+
config = createDefaultCcrConfig();
|
|
2372
|
+
} else {
|
|
2373
|
+
config = await configureCcrWithPreset(preset, scriptLang);
|
|
2374
|
+
}
|
|
2375
|
+
writeCcrConfig(config);
|
|
2376
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrConfigSuccess}`));
|
|
2377
|
+
await configureCcrProxy(config);
|
|
2378
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.proxyConfigSuccess}`));
|
|
2379
|
+
await restartAndCheckCcrStatus(scriptLang);
|
|
2380
|
+
showConfigurationTips(scriptLang, config.APIKEY);
|
|
2381
|
+
try {
|
|
2382
|
+
addCompletedOnboarding();
|
|
2383
|
+
} catch (error) {
|
|
2384
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
2385
|
+
}
|
|
2386
|
+
return true;
|
|
2387
|
+
} catch (error) {
|
|
2388
|
+
if (error.name === "ExitPromptError") {
|
|
2389
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2390
|
+
return false;
|
|
2391
|
+
}
|
|
2392
|
+
console.error(ansis.red(`${i18n.ccr.ccrConfigFailed}:`), error);
|
|
2393
|
+
return false;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
async function configureCcrFeature(scriptLang) {
|
|
2397
|
+
const i18n = getTranslation(scriptLang);
|
|
2398
|
+
const backupDir = backupExistingConfig();
|
|
2399
|
+
if (backupDir) {
|
|
2400
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2401
|
+
}
|
|
2402
|
+
await setupCcrConfiguration(scriptLang);
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
function format(template, replacements) {
|
|
2406
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
2407
|
+
return replacements[key] || match;
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
const execAsync$3 = promisify$1(exec$2);
|
|
2412
|
+
async function getInstalledVersion(command) {
|
|
2413
|
+
try {
|
|
2414
|
+
let stdout;
|
|
2415
|
+
try {
|
|
2416
|
+
const result = await execAsync$3(`${command} -v`);
|
|
2417
|
+
stdout = result.stdout;
|
|
2418
|
+
} catch {
|
|
2419
|
+
const result = await execAsync$3(`${command} --version`);
|
|
2420
|
+
stdout = result.stdout;
|
|
2421
|
+
}
|
|
2422
|
+
const versionMatch = stdout.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
2423
|
+
return versionMatch ? versionMatch[1] : null;
|
|
2424
|
+
} catch {
|
|
2425
|
+
return null;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
async function getLatestVersion(packageName) {
|
|
2429
|
+
try {
|
|
2430
|
+
const { stdout } = await execAsync$3(`npm view ${packageName} version`);
|
|
2431
|
+
return stdout.trim();
|
|
2432
|
+
} catch {
|
|
2433
|
+
return null;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
function compareVersions(current, latest) {
|
|
2437
|
+
if (!semver.valid(current) || !semver.valid(latest)) {
|
|
2438
|
+
return -1;
|
|
2439
|
+
}
|
|
2440
|
+
return semver.compare(current, latest);
|
|
2441
|
+
}
|
|
2442
|
+
function shouldUpdate(current, latest) {
|
|
2443
|
+
return compareVersions(current, latest) < 0;
|
|
2444
|
+
}
|
|
2445
|
+
async function checkCcrVersion() {
|
|
2446
|
+
const currentVersion = await getInstalledVersion("ccr");
|
|
2447
|
+
const latestVersion = await getLatestVersion("@musistudio/claude-code-router");
|
|
2448
|
+
return {
|
|
2449
|
+
installed: currentVersion !== null,
|
|
2171
2450
|
currentVersion,
|
|
2172
2451
|
latestVersion,
|
|
2173
2452
|
needsUpdate: currentVersion && latestVersion ? shouldUpdate(currentVersion, latestVersion) : false
|
|
@@ -2184,8 +2463,8 @@ async function checkClaudeCodeVersion() {
|
|
|
2184
2463
|
};
|
|
2185
2464
|
}
|
|
2186
2465
|
async function checkCometixLineVersion() {
|
|
2187
|
-
const currentVersion = await getInstalledVersion("
|
|
2188
|
-
const latestVersion = await getLatestVersion("
|
|
2466
|
+
const currentVersion = await getInstalledVersion("ccline");
|
|
2467
|
+
const latestVersion = await getLatestVersion("@cometix/ccline");
|
|
2189
2468
|
return {
|
|
2190
2469
|
installed: currentVersion !== null,
|
|
2191
2470
|
currentVersion,
|
|
@@ -2194,7 +2473,7 @@ async function checkCometixLineVersion() {
|
|
|
2194
2473
|
};
|
|
2195
2474
|
}
|
|
2196
2475
|
|
|
2197
|
-
const execAsync$
|
|
2476
|
+
const execAsync$2 = promisify$1(exec$2);
|
|
2198
2477
|
async function updateCcr(scriptLang, force = false) {
|
|
2199
2478
|
const i18n = getTranslation(scriptLang);
|
|
2200
2479
|
const spinner = ora(i18n.updater.checkingVersion).start();
|
|
@@ -2227,7 +2506,7 @@ async function updateCcr(scriptLang, force = false) {
|
|
|
2227
2506
|
}
|
|
2228
2507
|
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCR" })).start();
|
|
2229
2508
|
try {
|
|
2230
|
-
await execAsync$
|
|
2509
|
+
await execAsync$2("npm update -g @musistudio/claude-code-router");
|
|
2231
2510
|
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCR" }));
|
|
2232
2511
|
return true;
|
|
2233
2512
|
} catch (error) {
|
|
@@ -2273,7 +2552,7 @@ async function updateClaudeCode(scriptLang, force = false) {
|
|
|
2273
2552
|
}
|
|
2274
2553
|
const updateSpinner = ora(format(i18n.updater.updating, { tool: "Claude Code" })).start();
|
|
2275
2554
|
try {
|
|
2276
|
-
await execAsync$
|
|
2555
|
+
await execAsync$2("npm update -g @anthropic-ai/claude-code");
|
|
2277
2556
|
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "Claude Code" }));
|
|
2278
2557
|
return true;
|
|
2279
2558
|
} catch (error) {
|
|
@@ -2319,7 +2598,7 @@ async function updateCometixLine(scriptLang, force = false) {
|
|
|
2319
2598
|
}
|
|
2320
2599
|
const updateSpinner = ora(format(i18n.updater.updating, { tool: "CCometixLine" })).start();
|
|
2321
2600
|
try {
|
|
2322
|
-
await execAsync$
|
|
2601
|
+
await execAsync$2("cargo install ccometix");
|
|
2323
2602
|
updateSpinner.succeed(format(i18n.updater.updateSuccess, { tool: "CCometixLine" }));
|
|
2324
2603
|
return true;
|
|
2325
2604
|
} catch (error) {
|
|
@@ -2345,927 +2624,771 @@ async function checkAndUpdateTools(scriptLang) {
|
|
|
2345
2624
|
await updateCometixLine(scriptLang);
|
|
2346
2625
|
}
|
|
2347
2626
|
|
|
2348
|
-
|
|
2349
|
-
|
|
2627
|
+
const execAsync$1 = promisify(exec$1);
|
|
2628
|
+
async function isCcrInstalled() {
|
|
2629
|
+
let commandExists = false;
|
|
2630
|
+
try {
|
|
2631
|
+
await execAsync$1("ccr version");
|
|
2632
|
+
commandExists = true;
|
|
2633
|
+
} catch {
|
|
2634
|
+
try {
|
|
2635
|
+
await execAsync$1("which ccr");
|
|
2636
|
+
commandExists = true;
|
|
2637
|
+
} catch {
|
|
2638
|
+
commandExists = false;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
let hasCorrectPackage = false;
|
|
2642
|
+
try {
|
|
2643
|
+
await execAsync$1("npm list -g @musistudio/claude-code-router");
|
|
2644
|
+
hasCorrectPackage = true;
|
|
2645
|
+
} catch {
|
|
2646
|
+
hasCorrectPackage = false;
|
|
2647
|
+
}
|
|
2648
|
+
return {
|
|
2649
|
+
isInstalled: commandExists,
|
|
2650
|
+
hasCorrectPackage
|
|
2651
|
+
};
|
|
2350
2652
|
}
|
|
2351
|
-
async function
|
|
2352
|
-
const i18n = getTranslation(
|
|
2353
|
-
const
|
|
2354
|
-
if (
|
|
2355
|
-
console.log(ansis.green(`\u2714 ${i18n.
|
|
2356
|
-
await
|
|
2653
|
+
async function installCcr(scriptLang) {
|
|
2654
|
+
const i18n = getTranslation(scriptLang);
|
|
2655
|
+
const { isInstalled, hasCorrectPackage } = await isCcrInstalled();
|
|
2656
|
+
if (hasCorrectPackage) {
|
|
2657
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2658
|
+
await updateCcr(scriptLang);
|
|
2357
2659
|
return;
|
|
2358
2660
|
}
|
|
2359
|
-
if (
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2661
|
+
if (isInstalled && !hasCorrectPackage) {
|
|
2662
|
+
try {
|
|
2663
|
+
await execAsync$1("npm list -g claude-code-router");
|
|
2664
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.detectedIncorrectPackage}`));
|
|
2665
|
+
try {
|
|
2666
|
+
await execAsync$1("npm uninstall -g claude-code-router");
|
|
2667
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.uninstalledIncorrectPackage}`));
|
|
2668
|
+
} catch (uninstallError) {
|
|
2669
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.failedToUninstallIncorrectPackage}`));
|
|
2670
|
+
}
|
|
2671
|
+
} catch {
|
|
2672
|
+
}
|
|
2365
2673
|
}
|
|
2366
|
-
console.log(i18n.
|
|
2674
|
+
console.log(ansis.cyan(`\u{1F4E6} ${i18n.ccr.installingCcr}`));
|
|
2367
2675
|
try {
|
|
2368
|
-
await
|
|
2369
|
-
console.log(`\u2714 ${i18n.
|
|
2370
|
-
if (isTermux()) {
|
|
2371
|
-
console.log(ansis.gray(`
|
|
2372
|
-
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
2373
|
-
}
|
|
2676
|
+
await execAsync$1("npm install -g @musistudio/claude-code-router --force");
|
|
2677
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrInstallSuccess}`));
|
|
2374
2678
|
} catch (error) {
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
`));
|
|
2679
|
+
if (error.message?.includes("EEXIST")) {
|
|
2680
|
+
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2681
|
+
await updateCcr(scriptLang);
|
|
2682
|
+
return;
|
|
2380
2683
|
}
|
|
2684
|
+
console.error(ansis.red(`\u2716 ${i18n.ccr.ccrInstallFailed}`));
|
|
2381
2685
|
throw error;
|
|
2382
2686
|
}
|
|
2383
2687
|
}
|
|
2384
2688
|
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
${
|
|
2389
|
-
`
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
type: "
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
let aiOutputLang = lang;
|
|
2410
|
-
if (aiOutputLang === "custom") {
|
|
2411
|
-
const { customLang } = await inquirer.prompt({
|
|
2412
|
-
type: "input",
|
|
2413
|
-
name: "customLang",
|
|
2414
|
-
message: i18n.language.enterCustomLanguage,
|
|
2415
|
-
validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
|
|
2416
|
-
});
|
|
2417
|
-
if (!customLang) {
|
|
2418
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2419
|
-
process.exit(0);
|
|
2689
|
+
const COMETIX_PACKAGE_NAME = "@cometix/ccline";
|
|
2690
|
+
const COMETIX_COMMAND_NAME = "ccline";
|
|
2691
|
+
const COMETIX_COMMANDS = {
|
|
2692
|
+
CHECK_INSTALL: `npm list -g ${COMETIX_PACKAGE_NAME}`,
|
|
2693
|
+
INSTALL: `npm install -g ${COMETIX_PACKAGE_NAME}`,
|
|
2694
|
+
UPDATE: `npm update -g ${COMETIX_PACKAGE_NAME}`,
|
|
2695
|
+
PRINT_CONFIG: `${COMETIX_COMMAND_NAME} --print`
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
function getPlatformStatusLineConfig() {
|
|
2699
|
+
return {
|
|
2700
|
+
type: "command",
|
|
2701
|
+
command: isWindows() ? "%USERPROFILE%\\.claude\\ccline\\ccline.exe" : "~/.claude/ccline/ccline",
|
|
2702
|
+
padding: 0
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
function addCCometixLineConfig() {
|
|
2707
|
+
try {
|
|
2708
|
+
const statusLineConfig = getPlatformStatusLineConfig();
|
|
2709
|
+
let settings = {};
|
|
2710
|
+
if (exists(SETTINGS_FILE)) {
|
|
2711
|
+
settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
2420
2712
|
}
|
|
2421
|
-
|
|
2713
|
+
settings.statusLine = statusLineConfig;
|
|
2714
|
+
writeJsonConfig(SETTINGS_FILE, settings);
|
|
2715
|
+
return true;
|
|
2716
|
+
} catch (error) {
|
|
2717
|
+
console.error("Failed to add CCometixLine configuration:", error);
|
|
2718
|
+
return false;
|
|
2422
2719
|
}
|
|
2423
|
-
return aiOutputLang;
|
|
2424
2720
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
return
|
|
2721
|
+
function hasCCometixLineConfig() {
|
|
2722
|
+
try {
|
|
2723
|
+
if (!exists(SETTINGS_FILE)) {
|
|
2724
|
+
return false;
|
|
2725
|
+
}
|
|
2726
|
+
const settings = readJsonConfig(SETTINGS_FILE);
|
|
2727
|
+
return !!settings?.statusLine?.command?.includes("ccline");
|
|
2728
|
+
} catch (error) {
|
|
2729
|
+
return false;
|
|
2432
2730
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
if (!lang) {
|
|
2443
|
-
console.log(ansis.yellow("Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
2444
|
-
process.exit(0);
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
const execAsync = promisify(exec$1);
|
|
2734
|
+
async function isCometixLineInstalled() {
|
|
2735
|
+
try {
|
|
2736
|
+
await execAsync(COMETIX_COMMANDS.CHECK_INSTALL);
|
|
2737
|
+
return true;
|
|
2738
|
+
} catch {
|
|
2739
|
+
return false;
|
|
2445
2740
|
}
|
|
2446
|
-
const scriptLang = lang;
|
|
2447
|
-
updateZcfConfig({
|
|
2448
|
-
version,
|
|
2449
|
-
preferredLang: scriptLang
|
|
2450
|
-
});
|
|
2451
|
-
return scriptLang;
|
|
2452
2741
|
}
|
|
2453
|
-
async function
|
|
2742
|
+
async function installCometixLine(scriptLang) {
|
|
2454
2743
|
const i18n = getTranslation(scriptLang);
|
|
2455
|
-
|
|
2456
|
-
|
|
2744
|
+
const isInstalled = await isCometixLineInstalled();
|
|
2745
|
+
if (isInstalled) {
|
|
2746
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.cometixAlreadyInstalled}`));
|
|
2747
|
+
try {
|
|
2748
|
+
console.log(ansis.blue(`${i18n.cometix.installingOrUpdating}`));
|
|
2749
|
+
await execAsync(COMETIX_COMMANDS.INSTALL);
|
|
2750
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.installUpdateSuccess}`));
|
|
2751
|
+
} catch (error) {
|
|
2752
|
+
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.installUpdateFailed}: ${error}`));
|
|
2753
|
+
}
|
|
2754
|
+
if (!hasCCometixLineConfig()) {
|
|
2755
|
+
try {
|
|
2756
|
+
addCCometixLineConfig();
|
|
2757
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.statusLineConfigured || "Claude Code statusLine configured"}`));
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.statusLineConfigFailed || "Failed to configure statusLine"}: ${error}`));
|
|
2760
|
+
}
|
|
2761
|
+
} else {
|
|
2762
|
+
console.log(ansis.blue(`\u2139 ${i18n.cometix.statusLineAlreadyConfigured || "Claude Code statusLine already configured"}`));
|
|
2763
|
+
}
|
|
2764
|
+
return;
|
|
2457
2765
|
}
|
|
2458
|
-
|
|
2459
|
-
console.log(ansis.
|
|
2460
|
-
|
|
2766
|
+
try {
|
|
2767
|
+
console.log(ansis.blue(`${i18n.cometix.installingCometix}`));
|
|
2768
|
+
await execAsync(COMETIX_COMMANDS.INSTALL);
|
|
2769
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.cometixInstallSuccess}`));
|
|
2770
|
+
try {
|
|
2771
|
+
addCCometixLineConfig();
|
|
2772
|
+
console.log(ansis.green(`\u2714 ${i18n.cometix.statusLineConfigured || "Claude Code statusLine configured"}`));
|
|
2773
|
+
} catch (configError) {
|
|
2774
|
+
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.statusLineConfigFailed || "Failed to configure statusLine"}: ${configError}`));
|
|
2775
|
+
console.log(ansis.blue(`\u{1F4A1} ${i18n.cometix.statusLineManualConfig || "Please manually add statusLine configuration to Claude Code settings"}`));
|
|
2776
|
+
}
|
|
2777
|
+
} catch (error) {
|
|
2778
|
+
console.error(ansis.red(`\u2717 ${i18n.cometix.cometixInstallFailed}: ${error}`));
|
|
2779
|
+
throw error;
|
|
2461
2780
|
}
|
|
2462
|
-
return await selectAiOutputLanguage(scriptLang, scriptLang);
|
|
2463
2781
|
}
|
|
2464
2782
|
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
async function selectMcpServices(scriptLang) {
|
|
2473
|
-
const i18n = getTranslation(scriptLang);
|
|
2474
|
-
const choices = MCP_SERVICES.map((service) => ({
|
|
2475
|
-
name: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
|
|
2476
|
-
value: service.id,
|
|
2477
|
-
selected: false
|
|
2478
|
-
}));
|
|
2479
|
-
const { services } = await inquirer.prompt({
|
|
2480
|
-
type: "checkbox",
|
|
2481
|
-
name: "services",
|
|
2482
|
-
message: `${i18n.mcp.selectMcpServices}${i18n.common.multiSelectHint}`,
|
|
2483
|
-
choices
|
|
2484
|
-
});
|
|
2485
|
-
if (services === void 0) {
|
|
2486
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2487
|
-
return void 0;
|
|
2783
|
+
function validateApiKey(apiKey, lang = "zh-CN") {
|
|
2784
|
+
const i18n = getTranslation(lang);
|
|
2785
|
+
if (!apiKey || apiKey.trim() === "") {
|
|
2786
|
+
return {
|
|
2787
|
+
isValid: false,
|
|
2788
|
+
error: i18n.api.apiKeyValidation.empty
|
|
2789
|
+
};
|
|
2488
2790
|
}
|
|
2489
|
-
return
|
|
2791
|
+
return { isValid: true };
|
|
2490
2792
|
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
id: "sixStepsWorkflow",
|
|
2495
|
-
nameKey: "workflowOption.sixStepsWorkflow",
|
|
2496
|
-
descriptionKey: "workflowDescription.sixStepsWorkflow",
|
|
2497
|
-
defaultSelected: true,
|
|
2498
|
-
order: 1,
|
|
2499
|
-
commands: ["workflow.md"],
|
|
2500
|
-
agents: [],
|
|
2501
|
-
autoInstallAgents: false,
|
|
2502
|
-
category: "sixStep",
|
|
2503
|
-
outputDir: "workflow"
|
|
2504
|
-
},
|
|
2505
|
-
{
|
|
2506
|
-
id: "featPlanUx",
|
|
2507
|
-
nameKey: "workflowOption.featPlanUx",
|
|
2508
|
-
descriptionKey: "workflowDescription.featPlanUx",
|
|
2509
|
-
defaultSelected: true,
|
|
2510
|
-
order: 2,
|
|
2511
|
-
commands: ["feat.md"],
|
|
2512
|
-
agents: [
|
|
2513
|
-
{ id: "planner", filename: "planner.md", required: true },
|
|
2514
|
-
{ id: "ui-ux-designer", filename: "ui-ux-designer.md", required: true }
|
|
2515
|
-
],
|
|
2516
|
-
autoInstallAgents: true,
|
|
2517
|
-
category: "plan",
|
|
2518
|
-
outputDir: "feat"
|
|
2519
|
-
},
|
|
2520
|
-
{
|
|
2521
|
-
id: "gitWorkflow",
|
|
2522
|
-
nameKey: "workflowOption.gitWorkflow",
|
|
2523
|
-
descriptionKey: "workflowDescription.gitWorkflow",
|
|
2524
|
-
defaultSelected: true,
|
|
2525
|
-
order: 3,
|
|
2526
|
-
commands: ["git-commit.md", "git-rollback.md", "git-cleanBranches.md", "git-worktree.md"],
|
|
2527
|
-
agents: [],
|
|
2528
|
-
autoInstallAgents: false,
|
|
2529
|
-
category: "git",
|
|
2530
|
-
outputDir: "git"
|
|
2531
|
-
},
|
|
2532
|
-
{
|
|
2533
|
-
id: "bmadWorkflow",
|
|
2534
|
-
nameKey: "workflowOption.bmadWorkflow",
|
|
2535
|
-
descriptionKey: "workflowDescription.bmadWorkflow",
|
|
2536
|
-
defaultSelected: true,
|
|
2537
|
-
order: 4,
|
|
2538
|
-
commands: ["bmad-init.md"],
|
|
2539
|
-
agents: [],
|
|
2540
|
-
autoInstallAgents: false,
|
|
2541
|
-
category: "bmad",
|
|
2542
|
-
outputDir: "bmad"
|
|
2793
|
+
function formatApiKeyDisplay(apiKey) {
|
|
2794
|
+
if (!apiKey || apiKey.length < 12) {
|
|
2795
|
+
return apiKey;
|
|
2543
2796
|
}
|
|
2544
|
-
|
|
2545
|
-
function getWorkflowConfig(workflowId) {
|
|
2546
|
-
return WORKFLOW_CONFIGS.find((config) => config.id === workflowId);
|
|
2547
|
-
}
|
|
2548
|
-
function getOrderedWorkflows() {
|
|
2549
|
-
return [...WORKFLOW_CONFIGS].sort((a, b) => a.order - b.order);
|
|
2797
|
+
return `${apiKey.substring(0, 8)}...${apiKey.substring(apiKey.length - 4)}`;
|
|
2550
2798
|
}
|
|
2551
2799
|
|
|
2552
|
-
function
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
const { selectedWorkflows } = await inquirer.prompt({
|
|
2570
|
-
type: "checkbox",
|
|
2571
|
-
name: "selectedWorkflows",
|
|
2572
|
-
message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
|
|
2573
|
-
choices
|
|
2574
|
-
});
|
|
2575
|
-
if (!selectedWorkflows || selectedWorkflows.length === 0) {
|
|
2576
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2577
|
-
return;
|
|
2578
|
-
}
|
|
2579
|
-
await cleanupOldVersionFiles(scriptLang);
|
|
2580
|
-
for (const workflowId of selectedWorkflows) {
|
|
2581
|
-
const config = getWorkflowConfig(workflowId);
|
|
2582
|
-
if (config) {
|
|
2583
|
-
await installWorkflowWithDependencies(config, configLang, scriptLang);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
2588
|
-
const rootDir = getRootDir();
|
|
2589
|
-
const i18n = getTranslation(scriptLang);
|
|
2590
|
-
const result = {
|
|
2591
|
-
workflow: config.id,
|
|
2592
|
-
success: true,
|
|
2593
|
-
installedCommands: [],
|
|
2594
|
-
installedAgents: [],
|
|
2595
|
-
errors: []
|
|
2596
|
-
};
|
|
2597
|
-
const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
|
|
2598
|
-
console.log(ansis.cyan(`
|
|
2599
|
-
\u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
|
|
2600
|
-
const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
|
|
2601
|
-
if (!existsSync(commandsDir)) {
|
|
2602
|
-
await mkdir(commandsDir, { recursive: true });
|
|
2603
|
-
}
|
|
2604
|
-
for (const commandFile of config.commands) {
|
|
2605
|
-
const commandSource = join(rootDir, "templates", configLang, "workflow", config.category, "commands", commandFile);
|
|
2606
|
-
const destFileName = commandFile;
|
|
2607
|
-
const commandDest = join(commandsDir, destFileName);
|
|
2608
|
-
if (existsSync(commandSource)) {
|
|
2609
|
-
try {
|
|
2610
|
-
await copyFile$1(commandSource, commandDest);
|
|
2611
|
-
result.installedCommands.push(destFileName);
|
|
2612
|
-
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
|
|
2613
|
-
} catch (error) {
|
|
2614
|
-
const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
2615
|
-
result.errors?.push(errorMsg);
|
|
2616
|
-
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
2617
|
-
result.success = false;
|
|
2618
|
-
}
|
|
2619
|
-
}
|
|
2620
|
-
}
|
|
2621
|
-
if (config.autoInstallAgents && config.agents.length > 0) {
|
|
2622
|
-
const agentsCategoryDir = join(CLAUDE_DIR, "agents", "zcf", config.category);
|
|
2623
|
-
if (!existsSync(agentsCategoryDir)) {
|
|
2624
|
-
await mkdir(agentsCategoryDir, { recursive: true });
|
|
2625
|
-
}
|
|
2626
|
-
for (const agent of config.agents) {
|
|
2627
|
-
const agentSource = join(rootDir, "templates", configLang, "workflow", config.category, "agents", agent.filename);
|
|
2628
|
-
const agentDest = join(agentsCategoryDir, agent.filename);
|
|
2629
|
-
if (existsSync(agentSource)) {
|
|
2630
|
-
try {
|
|
2631
|
-
await copyFile$1(agentSource, agentDest);
|
|
2632
|
-
result.installedAgents.push(agent.filename);
|
|
2633
|
-
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
2634
|
-
} catch (error) {
|
|
2635
|
-
const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
2636
|
-
result.errors?.push(errorMsg);
|
|
2637
|
-
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
2638
|
-
if (agent.required) {
|
|
2639
|
-
result.success = false;
|
|
2640
|
-
}
|
|
2800
|
+
async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
|
|
2801
|
+
let authType = preselectedAuthType;
|
|
2802
|
+
if (!authType) {
|
|
2803
|
+
const { authType: selectedAuthType } = await inquirer.prompt({
|
|
2804
|
+
type: "list",
|
|
2805
|
+
name: "authType",
|
|
2806
|
+
message: i18n.api.configureApi,
|
|
2807
|
+
choices: addNumbersToChoices([
|
|
2808
|
+
{
|
|
2809
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
2810
|
+
value: "auth_token",
|
|
2811
|
+
short: i18n.api.useAuthToken
|
|
2812
|
+
},
|
|
2813
|
+
{
|
|
2814
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
2815
|
+
value: "api_key",
|
|
2816
|
+
short: i18n.api.useApiKey
|
|
2641
2817
|
}
|
|
2642
|
-
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
if (config.id === "bmadWorkflow") {
|
|
2648
|
-
console.log(ansis.cyan(`
|
|
2649
|
-
${i18n.workflow.bmadInitPrompt}`));
|
|
2818
|
+
])
|
|
2819
|
+
});
|
|
2820
|
+
if (!selectedAuthType) {
|
|
2821
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2822
|
+
return null;
|
|
2650
2823
|
}
|
|
2651
|
-
|
|
2652
|
-
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflow.workflowInstallError}`));
|
|
2824
|
+
authType = selectedAuthType;
|
|
2653
2825
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
const oldCommandFiles = [
|
|
2661
|
-
join(CLAUDE_DIR, "commands", "workflow.md"),
|
|
2662
|
-
join(CLAUDE_DIR, "commands", "feat.md")
|
|
2663
|
-
];
|
|
2664
|
-
const oldAgentFiles = [
|
|
2665
|
-
join(CLAUDE_DIR, "agents", "planner.md"),
|
|
2666
|
-
join(CLAUDE_DIR, "agents", "ui-ux-designer.md")
|
|
2667
|
-
];
|
|
2668
|
-
for (const file of oldCommandFiles) {
|
|
2669
|
-
if (existsSync(file)) {
|
|
2826
|
+
const { url } = await inquirer.prompt({
|
|
2827
|
+
type: "input",
|
|
2828
|
+
name: "url",
|
|
2829
|
+
message: i18n.api.enterApiUrl,
|
|
2830
|
+
validate: (value) => {
|
|
2831
|
+
if (!value) return i18n.api.urlRequired;
|
|
2670
2832
|
try {
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
} catch
|
|
2674
|
-
|
|
2833
|
+
new URL(value);
|
|
2834
|
+
return true;
|
|
2835
|
+
} catch {
|
|
2836
|
+
return i18n.api.invalidUrl;
|
|
2675
2837
|
}
|
|
2676
2838
|
}
|
|
2839
|
+
});
|
|
2840
|
+
if (url === void 0) {
|
|
2841
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2842
|
+
return null;
|
|
2677
2843
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2844
|
+
const keyMessage = authType === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
|
|
2845
|
+
const { key } = await inquirer.prompt({
|
|
2846
|
+
type: "input",
|
|
2847
|
+
name: "key",
|
|
2848
|
+
message: keyMessage,
|
|
2849
|
+
validate: (value) => {
|
|
2850
|
+
if (!value) {
|
|
2851
|
+
return i18n.api.keyRequired;
|
|
2852
|
+
}
|
|
2853
|
+
const validation = validateApiKey(value, scriptLang);
|
|
2854
|
+
if (!validation.isValid) {
|
|
2855
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
2685
2856
|
}
|
|
2857
|
+
return true;
|
|
2686
2858
|
}
|
|
2859
|
+
});
|
|
2860
|
+
if (key === void 0) {
|
|
2861
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2862
|
+
return null;
|
|
2687
2863
|
}
|
|
2864
|
+
console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
|
|
2865
|
+
return { url, key, authType };
|
|
2688
2866
|
}
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
await execAsync$2("ccr version");
|
|
2695
|
-
commandExists = true;
|
|
2696
|
-
} catch {
|
|
2697
|
-
try {
|
|
2698
|
-
await execAsync$2("which ccr");
|
|
2699
|
-
commandExists = true;
|
|
2700
|
-
} catch {
|
|
2701
|
-
commandExists = false;
|
|
2702
|
-
}
|
|
2703
|
-
}
|
|
2704
|
-
let hasCorrectPackage = false;
|
|
2705
|
-
try {
|
|
2706
|
-
await execAsync$2("npm list -g @musistudio/claude-code-router");
|
|
2707
|
-
hasCorrectPackage = true;
|
|
2708
|
-
} catch {
|
|
2709
|
-
hasCorrectPackage = false;
|
|
2867
|
+
async function modifyApiConfigPartially(existingConfig, i18n, scriptLang) {
|
|
2868
|
+
let currentConfig = { ...existingConfig };
|
|
2869
|
+
const latestConfig = getExistingApiConfig();
|
|
2870
|
+
if (latestConfig) {
|
|
2871
|
+
currentConfig = latestConfig;
|
|
2710
2872
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2873
|
+
const { item } = await inquirer.prompt({
|
|
2874
|
+
type: "list",
|
|
2875
|
+
name: "item",
|
|
2876
|
+
message: i18n.api.selectModifyItems,
|
|
2877
|
+
choices: addNumbersToChoices([
|
|
2878
|
+
{ name: i18n.api.modifyApiUrl, value: "url" },
|
|
2879
|
+
{ name: i18n.api.modifyApiKey, value: "key" },
|
|
2880
|
+
{ name: i18n.api.modifyAuthType, value: "authType" }
|
|
2881
|
+
])
|
|
2882
|
+
});
|
|
2883
|
+
if (!item) {
|
|
2884
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2722
2885
|
return;
|
|
2723
2886
|
}
|
|
2724
|
-
if (
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
try {
|
|
2739
|
-
await execAsync$2("npm install -g @musistudio/claude-code-router --force");
|
|
2740
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrInstallSuccess}`));
|
|
2741
|
-
} catch (error) {
|
|
2742
|
-
if (error.message?.includes("EEXIST")) {
|
|
2743
|
-
console.log(ansis.yellow(`\u26A0 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
2744
|
-
await updateCcr(scriptLang);
|
|
2745
|
-
return;
|
|
2746
|
-
}
|
|
2747
|
-
console.error(ansis.red(`\u2716 ${i18n.ccr.ccrInstallFailed}`));
|
|
2748
|
-
throw error;
|
|
2749
|
-
}
|
|
2750
|
-
}
|
|
2751
|
-
|
|
2752
|
-
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
2753
|
-
async function fetchProviderPresets() {
|
|
2754
|
-
try {
|
|
2755
|
-
const controller = new AbortController();
|
|
2756
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2757
|
-
const response = await fetch(PROVIDER_PRESETS_URL, {
|
|
2758
|
-
signal: controller.signal
|
|
2759
|
-
});
|
|
2760
|
-
clearTimeout(timeoutId);
|
|
2761
|
-
if (!response.ok) {
|
|
2762
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
2763
|
-
}
|
|
2764
|
-
const data = await response.json();
|
|
2765
|
-
const presets = [];
|
|
2766
|
-
if (Array.isArray(data)) {
|
|
2767
|
-
for (const provider of data) {
|
|
2768
|
-
if (provider && typeof provider === "object") {
|
|
2769
|
-
presets.push({
|
|
2770
|
-
name: provider.name || "",
|
|
2771
|
-
provider: provider.name || "",
|
|
2772
|
-
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2773
|
-
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2774
|
-
models: provider.models || [],
|
|
2775
|
-
description: provider.description || provider.name || "",
|
|
2776
|
-
transformer: provider.transformer
|
|
2777
|
-
});
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
} else if (data && typeof data === "object") {
|
|
2781
|
-
for (const [key, value] of Object.entries(data)) {
|
|
2782
|
-
if (typeof value === "object" && value !== null) {
|
|
2783
|
-
const provider = value;
|
|
2784
|
-
presets.push({
|
|
2785
|
-
name: provider.name || key,
|
|
2786
|
-
provider: key,
|
|
2787
|
-
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
2788
|
-
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
2789
|
-
models: provider.models || [],
|
|
2790
|
-
description: provider.description || "",
|
|
2791
|
-
transformer: provider.transformer
|
|
2792
|
-
});
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
return presets;
|
|
2797
|
-
} catch (error) {
|
|
2798
|
-
return getFallbackPresets();
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
function getFallbackPresets() {
|
|
2802
|
-
return [
|
|
2803
|
-
{
|
|
2804
|
-
name: "dashscope",
|
|
2805
|
-
provider: "dashscope",
|
|
2806
|
-
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
2807
|
-
requiresApiKey: true,
|
|
2808
|
-
models: ["qwen3-coder-plus"],
|
|
2809
|
-
description: "Alibaba DashScope",
|
|
2810
|
-
transformer: {
|
|
2811
|
-
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2812
|
-
"qwen3-coder-plus": {
|
|
2813
|
-
use: ["enhancetool"]
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
},
|
|
2817
|
-
{
|
|
2818
|
-
name: "deepseek",
|
|
2819
|
-
provider: "deepseek",
|
|
2820
|
-
baseURL: "https://api.deepseek.com/chat/completions",
|
|
2821
|
-
requiresApiKey: true,
|
|
2822
|
-
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
2823
|
-
description: "DeepSeek AI models",
|
|
2824
|
-
transformer: {
|
|
2825
|
-
use: ["deepseek"],
|
|
2826
|
-
"deepseek-chat": {
|
|
2827
|
-
use: ["tooluse"]
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
|
-
},
|
|
2831
|
-
{
|
|
2832
|
-
name: "gemini",
|
|
2833
|
-
provider: "gemini",
|
|
2834
|
-
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
2835
|
-
requiresApiKey: true,
|
|
2836
|
-
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
2837
|
-
description: "Google Gemini models",
|
|
2838
|
-
transformer: {
|
|
2839
|
-
use: ["gemini"]
|
|
2840
|
-
}
|
|
2841
|
-
},
|
|
2842
|
-
{
|
|
2843
|
-
name: "modelscope",
|
|
2844
|
-
provider: "modelscope",
|
|
2845
|
-
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
2846
|
-
requiresApiKey: true,
|
|
2847
|
-
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
2848
|
-
description: "ModelScope AI models",
|
|
2849
|
-
transformer: {
|
|
2850
|
-
use: [["maxtoken", { max_tokens: 65536 }]],
|
|
2851
|
-
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
2852
|
-
use: ["enhancetool"]
|
|
2853
|
-
},
|
|
2854
|
-
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
2855
|
-
use: ["reasoning"]
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2858
|
-
},
|
|
2859
|
-
{
|
|
2860
|
-
name: "openrouter",
|
|
2861
|
-
provider: "openrouter",
|
|
2862
|
-
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
2863
|
-
requiresApiKey: true,
|
|
2864
|
-
models: [
|
|
2865
|
-
"google/gemini-2.5-pro-preview",
|
|
2866
|
-
"anthropic/claude-sonnet-4",
|
|
2867
|
-
"anthropic/claude-3.5-sonnet",
|
|
2868
|
-
"anthropic/claude-3.7-sonnet:thinking"
|
|
2869
|
-
],
|
|
2870
|
-
description: "OpenRouter API",
|
|
2871
|
-
transformer: {
|
|
2872
|
-
use: ["openrouter"]
|
|
2873
|
-
}
|
|
2874
|
-
},
|
|
2875
|
-
{
|
|
2876
|
-
name: "siliconflow",
|
|
2877
|
-
provider: "siliconflow",
|
|
2878
|
-
baseURL: "https://api.siliconflow.cn/v1/chat/completions",
|
|
2879
|
-
requiresApiKey: true,
|
|
2880
|
-
models: ["moonshotai/Kimi-K2-Instruct"],
|
|
2881
|
-
description: "SiliconFlow AI",
|
|
2882
|
-
transformer: {
|
|
2883
|
-
use: [["maxtoken", { max_tokens: 16384 }]]
|
|
2887
|
+
if (item === "url") {
|
|
2888
|
+
const { url } = await inquirer.prompt({
|
|
2889
|
+
type: "input",
|
|
2890
|
+
name: "url",
|
|
2891
|
+
message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
|
|
2892
|
+
default: currentConfig.url,
|
|
2893
|
+
validate: (value) => {
|
|
2894
|
+
if (!value) return i18n.api.urlRequired;
|
|
2895
|
+
try {
|
|
2896
|
+
new URL(value);
|
|
2897
|
+
return true;
|
|
2898
|
+
} catch {
|
|
2899
|
+
return i18n.api.invalidUrl;
|
|
2900
|
+
}
|
|
2884
2901
|
}
|
|
2885
|
-
}
|
|
2886
|
-
{
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2902
|
+
});
|
|
2903
|
+
if (url === void 0) {
|
|
2904
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
currentConfig.url = url;
|
|
2908
|
+
const savedConfig = configureApi(currentConfig);
|
|
2909
|
+
if (savedConfig) {
|
|
2910
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2911
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${savedConfig.url}`));
|
|
2912
|
+
}
|
|
2913
|
+
} else if (item === "key") {
|
|
2914
|
+
const authType = currentConfig.authType || "auth_token";
|
|
2915
|
+
const keyMessage = authType === "auth_token" ? i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none) : i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none);
|
|
2916
|
+
const { key } = await inquirer.prompt({
|
|
2917
|
+
type: "input",
|
|
2918
|
+
name: "key",
|
|
2919
|
+
message: keyMessage,
|
|
2920
|
+
validate: (value) => {
|
|
2921
|
+
if (!value) {
|
|
2922
|
+
return i18n.api.keyRequired;
|
|
2923
|
+
}
|
|
2924
|
+
const validation = validateApiKey(value, scriptLang);
|
|
2925
|
+
if (!validation.isValid) {
|
|
2926
|
+
return validation.error || i18n.api.invalidKeyFormat;
|
|
2927
|
+
}
|
|
2928
|
+
return true;
|
|
2895
2929
|
}
|
|
2930
|
+
});
|
|
2931
|
+
if (key === void 0) {
|
|
2932
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2933
|
+
return;
|
|
2896
2934
|
}
|
|
2897
|
-
|
|
2935
|
+
currentConfig.key = key;
|
|
2936
|
+
const savedConfig = configureApi(currentConfig);
|
|
2937
|
+
if (savedConfig) {
|
|
2938
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2939
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
|
|
2940
|
+
}
|
|
2941
|
+
} else if (item === "authType") {
|
|
2942
|
+
const { authType } = await inquirer.prompt({
|
|
2943
|
+
type: "list",
|
|
2944
|
+
name: "authType",
|
|
2945
|
+
message: i18n.api.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.common.none),
|
|
2946
|
+
choices: addNumbersToChoices([
|
|
2947
|
+
{ name: "Auth Token (OAuth)", value: "auth_token" },
|
|
2948
|
+
{ name: "API Key", value: "api_key" }
|
|
2949
|
+
]),
|
|
2950
|
+
default: currentConfig.authType === "api_key" ? 1 : 0
|
|
2951
|
+
});
|
|
2952
|
+
if (authType === void 0) {
|
|
2953
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
currentConfig.authType = authType;
|
|
2957
|
+
const savedConfig = configureApi(currentConfig);
|
|
2958
|
+
if (savedConfig) {
|
|
2959
|
+
console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
|
|
2960
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${savedConfig.authType}`));
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
|
|
2965
|
+
const i18n = getTranslation(scriptLang);
|
|
2966
|
+
const backupDir = backupExistingConfig();
|
|
2967
|
+
if (backupDir) {
|
|
2968
|
+
console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
|
|
2969
|
+
}
|
|
2970
|
+
copyConfigFiles(configLang, true);
|
|
2971
|
+
if (aiOutputLang) {
|
|
2972
|
+
applyAiLanguageDirective(aiOutputLang);
|
|
2973
|
+
}
|
|
2974
|
+
await configureAiPersonality(scriptLang);
|
|
2975
|
+
console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
|
|
2976
|
+
console.log("\n" + ansis.cyan(i18n.common.complete));
|
|
2898
2977
|
}
|
|
2899
2978
|
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
const
|
|
2903
|
-
const
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2979
|
+
function handleExitPromptError(error) {
|
|
2980
|
+
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
2981
|
+
const zcfConfig = readZcfConfig();
|
|
2982
|
+
const defaultLang = zcfConfig?.preferredLang || "zh-CN";
|
|
2983
|
+
const i18n = getTranslation(defaultLang);
|
|
2984
|
+
console.log(ansis.cyan(`
|
|
2985
|
+
${i18n.common.goodbye}
|
|
2986
|
+
`));
|
|
2987
|
+
process.exit(0);
|
|
2907
2988
|
}
|
|
2989
|
+
return false;
|
|
2908
2990
|
}
|
|
2909
|
-
function
|
|
2910
|
-
const
|
|
2991
|
+
function handleGeneralError(error, lang) {
|
|
2992
|
+
const zcfConfig = readZcfConfig();
|
|
2993
|
+
const defaultLang = lang || zcfConfig?.preferredLang || "en";
|
|
2994
|
+
const i18n = getTranslation(defaultLang);
|
|
2995
|
+
const errorMsg = i18n.common.error || "Error";
|
|
2996
|
+
console.error(ansis.red(`${errorMsg}:`), error);
|
|
2997
|
+
if (error instanceof Error) {
|
|
2998
|
+
console.error(ansis.gray(`Stack: ${error.stack}`));
|
|
2999
|
+
}
|
|
3000
|
+
process.exit(1);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
async function isClaudeCodeInstalled() {
|
|
3004
|
+
return await commandExists("claude");
|
|
3005
|
+
}
|
|
3006
|
+
async function installClaudeCode(lang) {
|
|
3007
|
+
const i18n = getTranslation(lang);
|
|
3008
|
+
const installed = await isClaudeCodeInstalled();
|
|
3009
|
+
if (installed) {
|
|
3010
|
+
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
3011
|
+
await updateClaudeCode(lang);
|
|
3012
|
+
return;
|
|
3013
|
+
}
|
|
3014
|
+
if (isTermux()) {
|
|
3015
|
+
console.log(ansis.yellow(`\u2139 ${i18n.installation.termuxDetected}`));
|
|
3016
|
+
const termuxPrefix = getTermuxPrefix();
|
|
3017
|
+
console.log(ansis.gray(i18n.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
3018
|
+
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
3019
|
+
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
3020
|
+
}
|
|
3021
|
+
console.log(i18n.installation.installing);
|
|
2911
3022
|
try {
|
|
2912
|
-
|
|
2913
|
-
|
|
3023
|
+
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
3024
|
+
console.log(`\u2714 ${i18n.installation.installSuccess}`);
|
|
3025
|
+
if (isTermux()) {
|
|
3026
|
+
console.log(ansis.gray(`
|
|
3027
|
+
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
2914
3028
|
}
|
|
2915
|
-
const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
|
|
2916
|
-
const backupFileName = `config.json.${timestamp}.bak`;
|
|
2917
|
-
const backupPath = join$1(CCR_BACKUP_DIR, backupFileName);
|
|
2918
|
-
console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
|
|
2919
|
-
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
2920
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
|
|
2921
|
-
return backupPath;
|
|
2922
3029
|
} catch (error) {
|
|
2923
|
-
console.error(
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3030
|
+
console.error(`\u2716 ${i18n.installation.installFailed}`);
|
|
3031
|
+
if (isTermux()) {
|
|
3032
|
+
console.error(ansis.yellow(`
|
|
3033
|
+
${i18n.installation.termuxInstallHint}
|
|
3034
|
+
`));
|
|
3035
|
+
}
|
|
3036
|
+
throw error;
|
|
2930
3037
|
}
|
|
2931
|
-
return readJsonConfig(CCR_CONFIG_FILE);
|
|
2932
3038
|
}
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
const
|
|
2942
|
-
|
|
2943
|
-
|
|
3039
|
+
|
|
3040
|
+
async function selectMcpServices(scriptLang) {
|
|
3041
|
+
const i18n = getTranslation(scriptLang);
|
|
3042
|
+
const choices = MCP_SERVICES.map((service) => ({
|
|
3043
|
+
name: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
|
|
3044
|
+
value: service.id,
|
|
3045
|
+
selected: false
|
|
3046
|
+
}));
|
|
3047
|
+
const { services } = await inquirer.prompt({
|
|
3048
|
+
type: "checkbox",
|
|
3049
|
+
name: "services",
|
|
3050
|
+
message: `${i18n.mcp.selectMcpServices}${i18n.common.multiSelectHint}`,
|
|
3051
|
+
choices
|
|
3052
|
+
});
|
|
3053
|
+
if (services === void 0) {
|
|
3054
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3055
|
+
return void 0;
|
|
2944
3056
|
}
|
|
2945
|
-
|
|
2946
|
-
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
2947
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
3057
|
+
return services;
|
|
2948
3058
|
}
|
|
2949
|
-
|
|
3059
|
+
|
|
3060
|
+
async function selectAiOutputLanguage(scriptLang, defaultLang) {
|
|
2950
3061
|
const i18n = getTranslation(scriptLang);
|
|
2951
|
-
console.log(ansis.
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3062
|
+
console.log(ansis.dim(`
|
|
3063
|
+
${i18n.language.aiOutputLangHint}
|
|
3064
|
+
`));
|
|
3065
|
+
const aiLangChoices = Object.entries(AI_OUTPUT_LANGUAGES).map(([key, value]) => ({
|
|
3066
|
+
title: value.label,
|
|
3067
|
+
value: key
|
|
3068
|
+
}));
|
|
3069
|
+
const defaultChoice = defaultLang || (scriptLang === "zh-CN" ? "zh-CN" : "en");
|
|
3070
|
+
const { lang } = await inquirer.prompt({
|
|
3071
|
+
type: "list",
|
|
3072
|
+
name: "lang",
|
|
3073
|
+
message: i18n.language.selectAiOutputLang,
|
|
3074
|
+
choices: addNumbersToChoices(aiLangChoices.map((choice) => ({
|
|
3075
|
+
name: choice.title,
|
|
3076
|
+
value: choice.value
|
|
3077
|
+
}))),
|
|
3078
|
+
default: defaultChoice
|
|
3079
|
+
});
|
|
3080
|
+
if (!lang) {
|
|
3081
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3082
|
+
process.exit(0);
|
|
2956
3083
|
}
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
name: `${index + 2}. ${p.name}`,
|
|
2965
|
-
value: p
|
|
2966
|
-
}))
|
|
2967
|
-
];
|
|
2968
|
-
const { preset } = await inquirer.prompt({
|
|
2969
|
-
type: "list",
|
|
2970
|
-
name: "preset",
|
|
2971
|
-
message: i18n.ccr.selectCcrPreset,
|
|
2972
|
-
choices
|
|
3084
|
+
let aiOutputLang = lang;
|
|
3085
|
+
if (aiOutputLang === "custom") {
|
|
3086
|
+
const { customLang } = await inquirer.prompt({
|
|
3087
|
+
type: "input",
|
|
3088
|
+
name: "customLang",
|
|
3089
|
+
message: i18n.language.enterCustomLanguage,
|
|
3090
|
+
validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
|
|
2973
3091
|
});
|
|
2974
|
-
|
|
2975
|
-
} catch (error) {
|
|
2976
|
-
if (error.name === "ExitPromptError") {
|
|
3092
|
+
if (!customLang) {
|
|
2977
3093
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
2978
|
-
|
|
3094
|
+
process.exit(0);
|
|
2979
3095
|
}
|
|
2980
|
-
|
|
3096
|
+
return customLang;
|
|
2981
3097
|
}
|
|
3098
|
+
return aiOutputLang;
|
|
2982
3099
|
}
|
|
2983
|
-
async function
|
|
2984
|
-
const
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
// Use the original name from JSON
|
|
2988
|
-
api_base_url: preset.baseURL || "",
|
|
2989
|
-
api_key: "",
|
|
2990
|
-
models: preset.models
|
|
2991
|
-
};
|
|
2992
|
-
if (preset.transformer) {
|
|
2993
|
-
provider.transformer = preset.transformer;
|
|
3100
|
+
async function selectScriptLanguage(currentLang) {
|
|
3101
|
+
const zcfConfig = readZcfConfig();
|
|
3102
|
+
if (zcfConfig?.preferredLang) {
|
|
3103
|
+
return zcfConfig.preferredLang;
|
|
2994
3104
|
}
|
|
2995
|
-
if (
|
|
2996
|
-
|
|
2997
|
-
const { apiKey } = await inquirer.prompt({
|
|
2998
|
-
type: "input",
|
|
2999
|
-
name: "apiKey",
|
|
3000
|
-
message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
|
|
3001
|
-
validate: (value) => !!value || i18n.api.keyRequired
|
|
3002
|
-
});
|
|
3003
|
-
provider.api_key = apiKey;
|
|
3004
|
-
} catch (error) {
|
|
3005
|
-
if (error.name === "ExitPromptError") {
|
|
3006
|
-
throw error;
|
|
3007
|
-
}
|
|
3008
|
-
throw error;
|
|
3009
|
-
}
|
|
3010
|
-
} else {
|
|
3011
|
-
provider.api_key = "sk-free";
|
|
3105
|
+
if (currentLang) {
|
|
3106
|
+
return currentLang;
|
|
3012
3107
|
}
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
defaultModel = model;
|
|
3026
|
-
} catch (error) {
|
|
3027
|
-
if (error.name === "ExitPromptError") {
|
|
3028
|
-
throw error;
|
|
3029
|
-
}
|
|
3030
|
-
throw error;
|
|
3031
|
-
}
|
|
3108
|
+
const { lang } = await inquirer.prompt({
|
|
3109
|
+
type: "list",
|
|
3110
|
+
name: "lang",
|
|
3111
|
+
message: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
|
|
3112
|
+
choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
|
|
3113
|
+
name: LANG_LABELS[l],
|
|
3114
|
+
value: l
|
|
3115
|
+
})))
|
|
3116
|
+
});
|
|
3117
|
+
if (!lang) {
|
|
3118
|
+
console.log(ansis.yellow("Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"));
|
|
3119
|
+
process.exit(0);
|
|
3032
3120
|
}
|
|
3033
|
-
const
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
longContextThreshold: 6e4,
|
|
3040
|
-
webSearch: `${preset.name},${defaultModel}`
|
|
3041
|
-
};
|
|
3042
|
-
const config = {
|
|
3043
|
-
LOG: true,
|
|
3044
|
-
CLAUDE_PATH: "",
|
|
3045
|
-
HOST: "127.0.0.1",
|
|
3046
|
-
PORT: 3456,
|
|
3047
|
-
APIKEY: "sk-zcf-x-ccr",
|
|
3048
|
-
API_TIMEOUT_MS: "600000",
|
|
3049
|
-
PROXY_URL: "",
|
|
3050
|
-
transformers: [],
|
|
3051
|
-
Providers: [provider],
|
|
3052
|
-
Router: router
|
|
3053
|
-
};
|
|
3054
|
-
return config;
|
|
3121
|
+
const scriptLang = lang;
|
|
3122
|
+
updateZcfConfig({
|
|
3123
|
+
version,
|
|
3124
|
+
preferredLang: scriptLang
|
|
3125
|
+
});
|
|
3126
|
+
return scriptLang;
|
|
3055
3127
|
}
|
|
3056
|
-
async function
|
|
3128
|
+
async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfig) {
|
|
3057
3129
|
const i18n = getTranslation(scriptLang);
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
await execAsync$1("ccr restart");
|
|
3061
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestartSuccess}`));
|
|
3062
|
-
console.log(ansis.cyan(`${i18n.ccr.checkingCcrStatus}`));
|
|
3063
|
-
const { stdout } = await execAsync$1("ccr status");
|
|
3064
|
-
console.log(ansis.gray(stdout));
|
|
3065
|
-
} catch (error) {
|
|
3066
|
-
console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
|
|
3067
|
-
if (process.env.DEBUG) {
|
|
3068
|
-
console.error("Full error:", error);
|
|
3069
|
-
}
|
|
3130
|
+
if (commandLineOption) {
|
|
3131
|
+
return commandLineOption;
|
|
3070
3132
|
}
|
|
3133
|
+
if (savedConfig?.aiOutputLang) {
|
|
3134
|
+
console.log(ansis.gray(`\u2714 ${i18n.language.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
|
|
3135
|
+
return savedConfig.aiOutputLang;
|
|
3136
|
+
}
|
|
3137
|
+
return await selectAiOutputLanguage(scriptLang, scriptLang);
|
|
3071
3138
|
}
|
|
3072
|
-
|
|
3139
|
+
|
|
3140
|
+
const prompts = {
|
|
3141
|
+
__proto__: null,
|
|
3142
|
+
resolveAiOutputLanguage: resolveAiOutputLanguage,
|
|
3143
|
+
selectAiOutputLanguage: selectAiOutputLanguage,
|
|
3144
|
+
selectScriptLanguage: selectScriptLanguage
|
|
3145
|
+
};
|
|
3146
|
+
|
|
3147
|
+
function getRootDir() {
|
|
3148
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
3149
|
+
const distDir = dirname(dirname(currentFilePath));
|
|
3150
|
+
return dirname(distDir);
|
|
3151
|
+
}
|
|
3152
|
+
async function selectAndInstallWorkflows(configLang, scriptLang, preselectedWorkflows) {
|
|
3073
3153
|
const i18n = getTranslation(scriptLang);
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3154
|
+
const workflows = getOrderedWorkflows();
|
|
3155
|
+
const choices = workflows.map((workflow) => {
|
|
3156
|
+
const nameKey = workflow.id;
|
|
3157
|
+
const name = i18n.workflow.workflowOption[nameKey] || workflow.id;
|
|
3158
|
+
return {
|
|
3159
|
+
name,
|
|
3160
|
+
value: workflow.id,
|
|
3161
|
+
checked: workflow.defaultSelected
|
|
3162
|
+
};
|
|
3163
|
+
});
|
|
3164
|
+
let selectedWorkflows;
|
|
3165
|
+
if (preselectedWorkflows) {
|
|
3166
|
+
selectedWorkflows = preselectedWorkflows;
|
|
3167
|
+
} else {
|
|
3168
|
+
const response = await inquirer.prompt({
|
|
3169
|
+
type: "checkbox",
|
|
3170
|
+
name: "selectedWorkflows",
|
|
3171
|
+
message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
|
|
3172
|
+
choices
|
|
3173
|
+
});
|
|
3174
|
+
selectedWorkflows = response.selectedWorkflows;
|
|
3175
|
+
}
|
|
3176
|
+
if (!selectedWorkflows || selectedWorkflows.length === 0) {
|
|
3177
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3178
|
+
return;
|
|
3179
|
+
}
|
|
3180
|
+
await cleanupOldVersionFiles(scriptLang);
|
|
3181
|
+
for (const workflowId of selectedWorkflows) {
|
|
3182
|
+
const config = getWorkflowConfig(workflowId);
|
|
3183
|
+
if (config) {
|
|
3184
|
+
await installWorkflowWithDependencies(config, configLang, scriptLang);
|
|
3185
|
+
}
|
|
3082
3186
|
}
|
|
3083
|
-
console.log("");
|
|
3084
3187
|
}
|
|
3085
|
-
async function
|
|
3188
|
+
async function installWorkflowWithDependencies(config, configLang, scriptLang) {
|
|
3189
|
+
const rootDir = getRootDir();
|
|
3086
3190
|
const i18n = getTranslation(scriptLang);
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3191
|
+
const result = {
|
|
3192
|
+
workflow: config.id,
|
|
3193
|
+
success: true,
|
|
3194
|
+
installedCommands: [],
|
|
3195
|
+
installedAgents: [],
|
|
3196
|
+
errors: []
|
|
3197
|
+
};
|
|
3198
|
+
const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
|
|
3199
|
+
console.log(ansis.cyan(`
|
|
3200
|
+
\u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
|
|
3201
|
+
const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
|
|
3202
|
+
if (!existsSync(commandsDir)) {
|
|
3203
|
+
await mkdir(commandsDir, { recursive: true });
|
|
3204
|
+
}
|
|
3205
|
+
for (const commandFile of config.commands) {
|
|
3206
|
+
const commandSource = join(rootDir, "templates", configLang, "workflow", config.category, "commands", commandFile);
|
|
3207
|
+
const destFileName = commandFile;
|
|
3208
|
+
const commandDest = join(commandsDir, destFileName);
|
|
3209
|
+
if (existsSync(commandSource)) {
|
|
3092
3210
|
try {
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
message: i18n.ccr.overwriteCcrConfig,
|
|
3097
|
-
default: false
|
|
3098
|
-
});
|
|
3099
|
-
shouldBackupAndReconfigure = result.overwrite;
|
|
3211
|
+
await copyFile$1(commandSource, commandDest);
|
|
3212
|
+
result.installedCommands.push(destFileName);
|
|
3213
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
|
|
3100
3214
|
} catch (error) {
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
throw error;
|
|
3106
|
-
}
|
|
3107
|
-
if (!shouldBackupAndReconfigure) {
|
|
3108
|
-
console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
|
|
3109
|
-
await configureCcrProxy(existingConfig);
|
|
3110
|
-
return true;
|
|
3215
|
+
const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
|
|
3216
|
+
result.errors?.push(errorMsg);
|
|
3217
|
+
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
3218
|
+
result.success = false;
|
|
3111
3219
|
}
|
|
3112
|
-
backupCcrConfig(scriptLang);
|
|
3113
3220
|
}
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
if (preset === "skip") {
|
|
3120
|
-
console.log(ansis.yellow(`${i18n.ccr.skipConfiguring}`));
|
|
3121
|
-
config = {
|
|
3122
|
-
LOG: false,
|
|
3123
|
-
CLAUDE_PATH: "",
|
|
3124
|
-
HOST: "127.0.0.1",
|
|
3125
|
-
PORT: 3456,
|
|
3126
|
-
APIKEY: "sk-zcf-x-ccr",
|
|
3127
|
-
API_TIMEOUT_MS: "600000",
|
|
3128
|
-
PROXY_URL: "",
|
|
3129
|
-
transformers: [],
|
|
3130
|
-
Providers: [],
|
|
3131
|
-
// Empty providers array
|
|
3132
|
-
Router: {
|
|
3133
|
-
// Empty router configuration - user will configure in CCR UI
|
|
3134
|
-
}
|
|
3135
|
-
};
|
|
3136
|
-
} else {
|
|
3137
|
-
config = await configureCcrWithPreset(preset, scriptLang);
|
|
3221
|
+
}
|
|
3222
|
+
if (config.autoInstallAgents && config.agents.length > 0) {
|
|
3223
|
+
const agentsCategoryDir = join(CLAUDE_DIR, "agents", "zcf", config.category);
|
|
3224
|
+
if (!existsSync(agentsCategoryDir)) {
|
|
3225
|
+
await mkdir(agentsCategoryDir, { recursive: true });
|
|
3138
3226
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3227
|
+
for (const agent of config.agents) {
|
|
3228
|
+
const agentSource = join(rootDir, "templates", configLang, "workflow", config.category, "agents", agent.filename);
|
|
3229
|
+
const agentDest = join(agentsCategoryDir, agent.filename);
|
|
3230
|
+
if (existsSync(agentSource)) {
|
|
3231
|
+
try {
|
|
3232
|
+
await copyFile$1(agentSource, agentDest);
|
|
3233
|
+
result.installedAgents.push(agent.filename);
|
|
3234
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
|
|
3235
|
+
} catch (error) {
|
|
3236
|
+
const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
|
|
3237
|
+
result.errors?.push(errorMsg);
|
|
3238
|
+
console.error(ansis.red(` \u2717 ${errorMsg}`));
|
|
3239
|
+
if (agent.required) {
|
|
3240
|
+
result.success = false;
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3149
3244
|
}
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3245
|
+
}
|
|
3246
|
+
if (result.success) {
|
|
3247
|
+
console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflow.workflowInstallSuccess}`));
|
|
3248
|
+
if (config.id === "bmadWorkflow") {
|
|
3249
|
+
console.log(ansis.cyan(`
|
|
3250
|
+
${i18n.workflow.bmadInitPrompt}`));
|
|
3155
3251
|
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3252
|
+
} else {
|
|
3253
|
+
console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflow.workflowInstallError}`));
|
|
3158
3254
|
}
|
|
3255
|
+
return result;
|
|
3159
3256
|
}
|
|
3160
|
-
async function
|
|
3257
|
+
async function cleanupOldVersionFiles(scriptLang) {
|
|
3161
3258
|
const i18n = getTranslation(scriptLang);
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
};
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
type: "command",
|
|
3181
|
-
command: isWindows() ? "%USERPROFILE%\\.claude\\ccline\\ccline.exe" : "~/.claude/ccline/ccline",
|
|
3182
|
-
padding: 0
|
|
3183
|
-
};
|
|
3184
|
-
}
|
|
3185
|
-
|
|
3186
|
-
function addCCometixLineConfig() {
|
|
3187
|
-
try {
|
|
3188
|
-
const statusLineConfig = getPlatformStatusLineConfig();
|
|
3189
|
-
let settings = {};
|
|
3190
|
-
if (exists(SETTINGS_FILE)) {
|
|
3191
|
-
settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
3259
|
+
console.log(ansis.cyan(`
|
|
3260
|
+
\u{1F9F9} ${i18n.workflow.cleaningOldFiles || "Cleaning up old version files"}...`));
|
|
3261
|
+
const oldCommandFiles = [
|
|
3262
|
+
join(CLAUDE_DIR, "commands", "workflow.md"),
|
|
3263
|
+
join(CLAUDE_DIR, "commands", "feat.md")
|
|
3264
|
+
];
|
|
3265
|
+
const oldAgentFiles = [
|
|
3266
|
+
join(CLAUDE_DIR, "agents", "planner.md"),
|
|
3267
|
+
join(CLAUDE_DIR, "agents", "ui-ux-designer.md")
|
|
3268
|
+
];
|
|
3269
|
+
for (const file of oldCommandFiles) {
|
|
3270
|
+
if (existsSync(file)) {
|
|
3271
|
+
try {
|
|
3272
|
+
await rm(file, { force: true });
|
|
3273
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3274
|
+
} catch (error) {
|
|
3275
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3276
|
+
}
|
|
3192
3277
|
}
|
|
3193
|
-
settings.statusLine = statusLineConfig;
|
|
3194
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
3195
|
-
return true;
|
|
3196
|
-
} catch (error) {
|
|
3197
|
-
console.error("Failed to add CCometixLine configuration:", error);
|
|
3198
|
-
return false;
|
|
3199
3278
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3279
|
+
for (const file of oldAgentFiles) {
|
|
3280
|
+
if (existsSync(file)) {
|
|
3281
|
+
try {
|
|
3282
|
+
await rm(file, { force: true });
|
|
3283
|
+
console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3284
|
+
} catch (error) {
|
|
3285
|
+
console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
|
|
3286
|
+
}
|
|
3205
3287
|
}
|
|
3206
|
-
const settings = readJsonConfig(SETTINGS_FILE);
|
|
3207
|
-
return !!settings?.statusLine?.command?.includes("ccline");
|
|
3208
|
-
} catch (error) {
|
|
3209
|
-
return false;
|
|
3210
3288
|
}
|
|
3211
3289
|
}
|
|
3212
3290
|
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3291
|
+
function validateSkipPromptOptions(options) {
|
|
3292
|
+
if (options.allLang) {
|
|
3293
|
+
if (options.allLang === "zh-CN" || options.allLang === "en") {
|
|
3294
|
+
options.lang = options.allLang;
|
|
3295
|
+
options.configLang = options.allLang;
|
|
3296
|
+
options.aiOutputLang = options.allLang;
|
|
3297
|
+
} else {
|
|
3298
|
+
options.lang = "en";
|
|
3299
|
+
options.configLang = "en";
|
|
3300
|
+
options.aiOutputLang = options.allLang;
|
|
3301
|
+
}
|
|
3220
3302
|
}
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3303
|
+
if (!options.configAction) {
|
|
3304
|
+
options.configAction = "backup";
|
|
3305
|
+
}
|
|
3306
|
+
if (!options.lang) {
|
|
3307
|
+
options.lang = "en";
|
|
3308
|
+
}
|
|
3309
|
+
if (!options.configLang) {
|
|
3310
|
+
options.configLang = "en";
|
|
3311
|
+
}
|
|
3312
|
+
if (!options.aiOutputLang) {
|
|
3313
|
+
options.aiOutputLang = "en";
|
|
3314
|
+
}
|
|
3315
|
+
if (!options.aiPersonality) {
|
|
3316
|
+
options.aiPersonality = "professional";
|
|
3317
|
+
}
|
|
3318
|
+
if (typeof options.installCometixLine === "string") {
|
|
3319
|
+
options.installCometixLine = options.installCometixLine.toLowerCase() === "true";
|
|
3320
|
+
}
|
|
3321
|
+
if (options.installCometixLine === void 0) {
|
|
3322
|
+
options.installCometixLine = true;
|
|
3323
|
+
}
|
|
3324
|
+
if (options.configAction && !["new", "backup", "merge", "docs-only", "skip"].includes(options.configAction)) {
|
|
3325
|
+
throw new Error(
|
|
3326
|
+
`Invalid configAction value: ${options.configAction}. Must be 'new', 'backup', 'merge', 'docs-only', or 'skip'`
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
if (options.apiType && !["auth_token", "api_key", "ccr_proxy", "skip"].includes(options.apiType)) {
|
|
3330
|
+
throw new Error(
|
|
3331
|
+
`Invalid apiType value: ${options.apiType}. Must be 'auth_token', 'api_key', 'ccr_proxy', or 'skip'`
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
if (options.apiType === "api_key" && !options.apiKey) {
|
|
3335
|
+
throw new Error('API key is required when apiType is "api_key"');
|
|
3336
|
+
}
|
|
3337
|
+
if (options.apiType === "auth_token" && !options.apiKey) {
|
|
3338
|
+
throw new Error('API key is required when apiType is "auth_token"');
|
|
3339
|
+
}
|
|
3340
|
+
if (typeof options.mcpServices === "string") {
|
|
3341
|
+
if (options.mcpServices === "skip") {
|
|
3342
|
+
options.mcpServices = false;
|
|
3343
|
+
} else if (options.mcpServices === "all") {
|
|
3344
|
+
options.mcpServices = MCP_SERVICES.filter((s) => !s.requiresApiKey).map((s) => s.id);
|
|
3345
|
+
} else {
|
|
3346
|
+
options.mcpServices = options.mcpServices.split(",").map((s) => s.trim());
|
|
3233
3347
|
}
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3348
|
+
}
|
|
3349
|
+
if (Array.isArray(options.mcpServices)) {
|
|
3350
|
+
const validServices = MCP_SERVICES.map((s) => s.id);
|
|
3351
|
+
for (const service of options.mcpServices) {
|
|
3352
|
+
if (!validServices.includes(service)) {
|
|
3353
|
+
throw new Error(`Invalid MCP service: ${service}. Available services: ${validServices.join(", ")}`);
|
|
3240
3354
|
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
if (typeof options.workflows === "string") {
|
|
3358
|
+
if (options.workflows === "skip") {
|
|
3359
|
+
options.workflows = false;
|
|
3360
|
+
} else if (options.workflows === "all") {
|
|
3361
|
+
options.workflows = WORKFLOW_CONFIGS.map((w) => w.id);
|
|
3241
3362
|
} else {
|
|
3242
|
-
|
|
3363
|
+
options.workflows = options.workflows.split(",").map((s) => s.trim());
|
|
3243
3364
|
}
|
|
3244
|
-
return;
|
|
3245
3365
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
console.log(ansis.green(`\u2714 ${i18n.cometix.statusLineConfigured || "Claude Code statusLine configured"}`));
|
|
3253
|
-
} catch (configError) {
|
|
3254
|
-
console.warn(ansis.yellow(`\u26A0 ${i18n.cometix.statusLineConfigFailed || "Failed to configure statusLine"}: ${configError}`));
|
|
3255
|
-
console.log(ansis.blue(`\u{1F4A1} ${i18n.cometix.statusLineManualConfig || "Please manually add statusLine configuration to Claude Code settings"}`));
|
|
3366
|
+
if (Array.isArray(options.workflows)) {
|
|
3367
|
+
const validWorkflows = WORKFLOW_CONFIGS.map((w) => w.id);
|
|
3368
|
+
for (const workflow of options.workflows) {
|
|
3369
|
+
if (!validWorkflows.includes(workflow)) {
|
|
3370
|
+
throw new Error(`Invalid workflow: ${workflow}. Available workflows: ${validWorkflows.join(", ")}`);
|
|
3371
|
+
}
|
|
3256
3372
|
}
|
|
3257
|
-
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3373
|
+
}
|
|
3374
|
+
if (options.mcpServices === void 0) {
|
|
3375
|
+
options.mcpServices = "all";
|
|
3376
|
+
options.mcpServices = MCP_SERVICES.filter((s) => !s.requiresApiKey).map((s) => s.id);
|
|
3377
|
+
}
|
|
3378
|
+
if (options.workflows === void 0) {
|
|
3379
|
+
options.workflows = "all";
|
|
3380
|
+
options.workflows = WORKFLOW_CONFIGS.map((w) => w.id);
|
|
3260
3381
|
}
|
|
3261
3382
|
}
|
|
3262
|
-
|
|
3263
3383
|
async function init(options = {}) {
|
|
3384
|
+
if (options.skipPrompt) {
|
|
3385
|
+
validateSkipPromptOptions(options);
|
|
3386
|
+
}
|
|
3264
3387
|
try {
|
|
3265
3388
|
if (!options.skipBanner) {
|
|
3266
3389
|
displayBannerWithInfo();
|
|
3267
3390
|
}
|
|
3268
|
-
const scriptLang = await selectScriptLanguage(options.lang);
|
|
3391
|
+
const scriptLang = options.skipPrompt ? options.lang || "en" : await selectScriptLanguage(options.lang);
|
|
3269
3392
|
const i18n = I18N[scriptLang];
|
|
3270
3393
|
if (isTermux()) {
|
|
3271
3394
|
console.log(ansis.yellow(`
|
|
@@ -3273,40 +3396,48 @@ async function init(options = {}) {
|
|
|
3273
3396
|
console.log(ansis.gray(i18n.installation.termuxEnvironmentInfo));
|
|
3274
3397
|
}
|
|
3275
3398
|
let configLang = options.configLang;
|
|
3276
|
-
if (!configLang) {
|
|
3399
|
+
if (!configLang && !options.skipPrompt) {
|
|
3277
3400
|
const { lang } = await inquirer.prompt({
|
|
3278
3401
|
type: "list",
|
|
3279
3402
|
name: "lang",
|
|
3280
3403
|
message: i18n.language.selectConfigLang,
|
|
3281
|
-
choices: addNumbersToChoices(
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3404
|
+
choices: addNumbersToChoices(
|
|
3405
|
+
SUPPORTED_LANGS.map((l) => ({
|
|
3406
|
+
name: `${LANG_LABELS[l]} - ${i18n.language.configLangHint[l]}`,
|
|
3407
|
+
value: l
|
|
3408
|
+
}))
|
|
3409
|
+
)
|
|
3285
3410
|
});
|
|
3286
3411
|
if (!lang) {
|
|
3287
3412
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3288
3413
|
process.exit(0);
|
|
3289
3414
|
}
|
|
3290
3415
|
configLang = lang;
|
|
3416
|
+
} else if (!configLang && options.skipPrompt) {
|
|
3417
|
+
configLang = "en";
|
|
3291
3418
|
}
|
|
3292
3419
|
const zcfConfig = readZcfConfig();
|
|
3293
|
-
const aiOutputLang = await resolveAiOutputLanguage(scriptLang, options.aiOutputLang, zcfConfig);
|
|
3420
|
+
const aiOutputLang = options.skipPrompt ? options.aiOutputLang || "en" : await resolveAiOutputLanguage(scriptLang, options.aiOutputLang, zcfConfig);
|
|
3294
3421
|
const installed = await isClaudeCodeInstalled();
|
|
3295
3422
|
if (!installed) {
|
|
3296
|
-
|
|
3297
|
-
type: "confirm",
|
|
3298
|
-
name: "shouldInstall",
|
|
3299
|
-
message: i18n.installation.installPrompt,
|
|
3300
|
-
default: true
|
|
3301
|
-
});
|
|
3302
|
-
if (shouldInstall === void 0) {
|
|
3303
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3304
|
-
process.exit(0);
|
|
3305
|
-
}
|
|
3306
|
-
if (shouldInstall) {
|
|
3423
|
+
if (options.skipPrompt) {
|
|
3307
3424
|
await installClaudeCode(scriptLang);
|
|
3308
3425
|
} else {
|
|
3309
|
-
|
|
3426
|
+
const { shouldInstall } = await inquirer.prompt({
|
|
3427
|
+
type: "confirm",
|
|
3428
|
+
name: "shouldInstall",
|
|
3429
|
+
message: i18n.installation.installPrompt,
|
|
3430
|
+
default: true
|
|
3431
|
+
});
|
|
3432
|
+
if (shouldInstall === void 0) {
|
|
3433
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3434
|
+
process.exit(0);
|
|
3435
|
+
}
|
|
3436
|
+
if (shouldInstall) {
|
|
3437
|
+
await installClaudeCode(scriptLang);
|
|
3438
|
+
} else {
|
|
3439
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
3440
|
+
}
|
|
3310
3441
|
}
|
|
3311
3442
|
} else {
|
|
3312
3443
|
console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
|
|
@@ -3314,129 +3445,182 @@ async function init(options = {}) {
|
|
|
3314
3445
|
ensureClaudeDir();
|
|
3315
3446
|
let action = "new";
|
|
3316
3447
|
if (existsSync(SETTINGS_FILE) && !options.force) {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
{ name: i18n.common.skip, value: "skip" }
|
|
3326
|
-
])
|
|
3327
|
-
});
|
|
3328
|
-
if (!userAction) {
|
|
3329
|
-
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3330
|
-
process.exit(0);
|
|
3331
|
-
}
|
|
3332
|
-
action = userAction;
|
|
3333
|
-
if (action === "skip") {
|
|
3334
|
-
console.log(ansis.yellow(i18n.common.skip));
|
|
3335
|
-
return;
|
|
3336
|
-
}
|
|
3337
|
-
}
|
|
3338
|
-
let apiConfig = null;
|
|
3339
|
-
const isNewInstall = !existsSync(SETTINGS_FILE);
|
|
3340
|
-
if (action !== "docs-only" && (isNewInstall || ["backup", "merge"].includes(action))) {
|
|
3341
|
-
const existingApiConfig = getExistingApiConfig();
|
|
3342
|
-
if (existingApiConfig) {
|
|
3343
|
-
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
3344
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
3345
|
-
console.log(
|
|
3346
|
-
ansis.gray(
|
|
3347
|
-
` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
|
|
3348
|
-
)
|
|
3349
|
-
);
|
|
3350
|
-
console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
3351
|
-
`));
|
|
3352
|
-
const { action: apiAction } = await inquirer.prompt({
|
|
3448
|
+
if (options.skipPrompt) {
|
|
3449
|
+
action = options.configAction || "backup";
|
|
3450
|
+
if (action === "skip") {
|
|
3451
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
3452
|
+
return;
|
|
3453
|
+
}
|
|
3454
|
+
} else {
|
|
3455
|
+
const { action: userAction } = await inquirer.prompt({
|
|
3353
3456
|
type: "list",
|
|
3354
3457
|
name: "action",
|
|
3355
|
-
message: i18n.
|
|
3458
|
+
message: i18n.configuration.existingConfig,
|
|
3356
3459
|
choices: addNumbersToChoices([
|
|
3357
|
-
{ name: i18n.
|
|
3358
|
-
{ name: i18n.
|
|
3359
|
-
{ name: i18n.
|
|
3360
|
-
{ name: i18n.
|
|
3361
|
-
{ name: i18n.api.skipApi, value: "skip" }
|
|
3460
|
+
{ name: i18n.configuration.backupAndOverwrite, value: "backup" },
|
|
3461
|
+
{ name: i18n.configuration.updateDocsOnly, value: "docs-only" },
|
|
3462
|
+
{ name: i18n.configuration.mergeConfig, value: "merge" },
|
|
3463
|
+
{ name: i18n.common.skip, value: "skip" }
|
|
3362
3464
|
])
|
|
3363
3465
|
});
|
|
3364
|
-
if (!
|
|
3466
|
+
if (!userAction) {
|
|
3365
3467
|
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3366
3468
|
process.exit(0);
|
|
3367
3469
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3470
|
+
action = userAction;
|
|
3471
|
+
if (action === "skip") {
|
|
3472
|
+
console.log(ansis.yellow(i18n.common.skip));
|
|
3473
|
+
return;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
} else if (options.skipPrompt && options.configAction) {
|
|
3477
|
+
action = options.configAction;
|
|
3478
|
+
}
|
|
3479
|
+
let apiConfig = null;
|
|
3480
|
+
const isNewInstall = !existsSync(SETTINGS_FILE);
|
|
3481
|
+
if (action !== "docs-only" && (isNewInstall || ["backup", "merge", "new"].includes(action))) {
|
|
3482
|
+
if (options.skipPrompt) {
|
|
3483
|
+
if (options.apiType === "auth_token" && options.apiKey) {
|
|
3484
|
+
apiConfig = {
|
|
3485
|
+
authType: "auth_token",
|
|
3486
|
+
key: options.apiKey,
|
|
3487
|
+
url: options.apiUrl || "https://api.anthropic.com"
|
|
3488
|
+
};
|
|
3489
|
+
} else if (options.apiType === "api_key" && options.apiKey) {
|
|
3490
|
+
apiConfig = {
|
|
3491
|
+
authType: "api_key",
|
|
3492
|
+
key: options.apiKey,
|
|
3493
|
+
url: options.apiUrl || "https://api.anthropic.com"
|
|
3494
|
+
};
|
|
3495
|
+
} else if (options.apiType === "ccr_proxy") {
|
|
3383
3496
|
const ccrStatus = await isCcrInstalled();
|
|
3384
3497
|
if (!ccrStatus.hasCorrectPackage) {
|
|
3385
3498
|
await installCcr(scriptLang);
|
|
3386
3499
|
} else {
|
|
3387
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr
|
|
3500
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr?.ccrAlreadyInstalled || "CCR already installed"}`));
|
|
3388
3501
|
}
|
|
3389
|
-
const
|
|
3390
|
-
if (
|
|
3391
|
-
|
|
3392
|
-
|
|
3502
|
+
const existingCcrConfig = readCcrConfig();
|
|
3503
|
+
if (existingCcrConfig) {
|
|
3504
|
+
const backupPath = backupCcrConfig(scriptLang);
|
|
3505
|
+
if (backupPath) {
|
|
3506
|
+
console.log(ansis.gray(`\u2714 ${i18n.ccr?.ccrBackupSuccess?.replace("{path}", backupPath) || "CCR configuration backed up"}`));
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
const defaultCcrConfig = createDefaultCcrConfig();
|
|
3510
|
+
writeCcrConfig(defaultCcrConfig);
|
|
3511
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr?.ccrConfigSuccess || "CCR configuration created"}`));
|
|
3512
|
+
await configureCcrProxy(defaultCcrConfig);
|
|
3513
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr?.proxyConfigSuccess || "Proxy settings configured"}`));
|
|
3514
|
+
try {
|
|
3515
|
+
addCompletedOnboarding();
|
|
3516
|
+
} catch (error) {
|
|
3517
|
+
console.error(ansis.red(i18n.configuration?.failedToSetOnboarding || "Failed to set onboarding flag"), error);
|
|
3393
3518
|
}
|
|
3519
|
+
apiConfig = null;
|
|
3394
3520
|
}
|
|
3395
3521
|
} else {
|
|
3396
|
-
const
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
{
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
name: i18n.api.
|
|
3418
|
-
value: "skip"
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
process.exit(0);
|
|
3425
|
-
}
|
|
3426
|
-
if (apiChoice === "ccr_proxy") {
|
|
3427
|
-
const ccrStatus = await isCcrInstalled();
|
|
3428
|
-
if (!ccrStatus.hasCorrectPackage) {
|
|
3429
|
-
await installCcr(scriptLang);
|
|
3430
|
-
} else {
|
|
3431
|
-
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3522
|
+
const existingApiConfig = getExistingApiConfig();
|
|
3523
|
+
if (existingApiConfig) {
|
|
3524
|
+
console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
|
|
3525
|
+
console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
|
|
3526
|
+
console.log(
|
|
3527
|
+
ansis.gray(
|
|
3528
|
+
` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
|
|
3529
|
+
)
|
|
3530
|
+
);
|
|
3531
|
+
console.log(
|
|
3532
|
+
ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
|
|
3533
|
+
`)
|
|
3534
|
+
);
|
|
3535
|
+
const { action: apiAction } = await inquirer.prompt({
|
|
3536
|
+
type: "list",
|
|
3537
|
+
name: "action",
|
|
3538
|
+
message: i18n.api.selectApiAction,
|
|
3539
|
+
choices: addNumbersToChoices([
|
|
3540
|
+
{ name: i18n.api.keepExistingConfig, value: "keep" },
|
|
3541
|
+
{ name: i18n.api.modifyAllConfig, value: "modify-all" },
|
|
3542
|
+
{ name: i18n.api.modifyPartialConfig, value: "modify-partial" },
|
|
3543
|
+
{ name: i18n.api.useCcrProxy, value: "use-ccr" },
|
|
3544
|
+
{ name: i18n.api.skipApi, value: "skip" }
|
|
3545
|
+
])
|
|
3546
|
+
});
|
|
3547
|
+
if (!apiAction) {
|
|
3548
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3549
|
+
process.exit(0);
|
|
3432
3550
|
}
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3551
|
+
if (apiAction === "keep" || apiAction === "skip") {
|
|
3552
|
+
apiConfig = null;
|
|
3553
|
+
if (apiAction === "keep") {
|
|
3554
|
+
try {
|
|
3555
|
+
addCompletedOnboarding();
|
|
3556
|
+
} catch (error) {
|
|
3557
|
+
console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
} else if (apiAction === "modify-partial") {
|
|
3561
|
+
await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
|
|
3436
3562
|
apiConfig = null;
|
|
3563
|
+
} else if (apiAction === "modify-all") {
|
|
3564
|
+
apiConfig = await configureApiCompletely(i18n, scriptLang);
|
|
3565
|
+
} else if (apiAction === "use-ccr") {
|
|
3566
|
+
const ccrStatus = await isCcrInstalled();
|
|
3567
|
+
if (!ccrStatus.hasCorrectPackage) {
|
|
3568
|
+
await installCcr(scriptLang);
|
|
3569
|
+
} else {
|
|
3570
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3571
|
+
}
|
|
3572
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3573
|
+
if (ccrConfigured) {
|
|
3574
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
3575
|
+
apiConfig = null;
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
} else {
|
|
3579
|
+
const { apiChoice } = await inquirer.prompt({
|
|
3580
|
+
type: "list",
|
|
3581
|
+
name: "apiChoice",
|
|
3582
|
+
message: i18n.api.configureApi,
|
|
3583
|
+
choices: [
|
|
3584
|
+
{
|
|
3585
|
+
name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
|
|
3586
|
+
value: "auth_token",
|
|
3587
|
+
short: i18n.api.useAuthToken
|
|
3588
|
+
},
|
|
3589
|
+
{
|
|
3590
|
+
name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
|
|
3591
|
+
value: "api_key",
|
|
3592
|
+
short: i18n.api.useApiKey
|
|
3593
|
+
},
|
|
3594
|
+
{
|
|
3595
|
+
name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
|
|
3596
|
+
value: "ccr_proxy",
|
|
3597
|
+
short: i18n.api.useCcrProxy
|
|
3598
|
+
},
|
|
3599
|
+
{
|
|
3600
|
+
name: i18n.api.skipApi,
|
|
3601
|
+
value: "skip"
|
|
3602
|
+
}
|
|
3603
|
+
]
|
|
3604
|
+
});
|
|
3605
|
+
if (!apiChoice) {
|
|
3606
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3607
|
+
process.exit(0);
|
|
3608
|
+
}
|
|
3609
|
+
if (apiChoice === "ccr_proxy") {
|
|
3610
|
+
const ccrStatus = await isCcrInstalled();
|
|
3611
|
+
if (!ccrStatus.hasCorrectPackage) {
|
|
3612
|
+
await installCcr(scriptLang);
|
|
3613
|
+
} else {
|
|
3614
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
|
|
3615
|
+
}
|
|
3616
|
+
const ccrConfigured = await setupCcrConfiguration(scriptLang);
|
|
3617
|
+
if (ccrConfigured) {
|
|
3618
|
+
console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
|
|
3619
|
+
apiConfig = null;
|
|
3620
|
+
}
|
|
3621
|
+
} else if (apiChoice !== "skip") {
|
|
3622
|
+
apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
|
|
3437
3623
|
}
|
|
3438
|
-
} else if (apiChoice !== "skip") {
|
|
3439
|
-
apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
|
|
3440
3624
|
}
|
|
3441
3625
|
}
|
|
3442
3626
|
}
|
|
@@ -3448,13 +3632,29 @@ async function init(options = {}) {
|
|
|
3448
3632
|
}
|
|
3449
3633
|
if (action === "docs-only") {
|
|
3450
3634
|
copyConfigFiles(configLang, true);
|
|
3451
|
-
|
|
3635
|
+
if (options.skipPrompt) {
|
|
3636
|
+
if (options.workflows !== false) {
|
|
3637
|
+
await selectAndInstallWorkflows(configLang, scriptLang, options.workflows);
|
|
3638
|
+
}
|
|
3639
|
+
} else {
|
|
3640
|
+
await selectAndInstallWorkflows(configLang, scriptLang);
|
|
3641
|
+
}
|
|
3452
3642
|
} else if (["backup", "merge", "new"].includes(action)) {
|
|
3453
3643
|
copyConfigFiles(configLang, false);
|
|
3454
|
-
|
|
3644
|
+
if (options.skipPrompt) {
|
|
3645
|
+
if (options.workflows !== false) {
|
|
3646
|
+
await selectAndInstallWorkflows(configLang, scriptLang, options.workflows);
|
|
3647
|
+
}
|
|
3648
|
+
} else {
|
|
3649
|
+
await selectAndInstallWorkflows(configLang, scriptLang);
|
|
3650
|
+
}
|
|
3455
3651
|
}
|
|
3456
3652
|
applyAiLanguageDirective(aiOutputLang);
|
|
3457
|
-
|
|
3653
|
+
if (options.skipPrompt) {
|
|
3654
|
+
await configureAiPersonality(scriptLang, options.aiPersonality);
|
|
3655
|
+
} else {
|
|
3656
|
+
await configureAiPersonality(scriptLang);
|
|
3657
|
+
}
|
|
3458
3658
|
if (apiConfig && action !== "docs-only") {
|
|
3459
3659
|
const configuredApi = configureApi(apiConfig);
|
|
3460
3660
|
if (configuredApi) {
|
|
@@ -3464,23 +3664,34 @@ async function init(options = {}) {
|
|
|
3464
3664
|
}
|
|
3465
3665
|
}
|
|
3466
3666
|
if (action !== "docs-only") {
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3667
|
+
let shouldConfigureMcp = false;
|
|
3668
|
+
if (options.skipPrompt) {
|
|
3669
|
+
shouldConfigureMcp = options.mcpServices !== false;
|
|
3670
|
+
} else {
|
|
3671
|
+
const { shouldConfigureMcp: userChoice } = await inquirer.prompt({
|
|
3672
|
+
type: "confirm",
|
|
3673
|
+
name: "shouldConfigureMcp",
|
|
3674
|
+
message: i18n.mcp.configureMcp,
|
|
3675
|
+
default: true
|
|
3676
|
+
});
|
|
3677
|
+
if (userChoice === void 0) {
|
|
3678
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3679
|
+
process.exit(0);
|
|
3680
|
+
}
|
|
3681
|
+
shouldConfigureMcp = userChoice;
|
|
3476
3682
|
}
|
|
3477
3683
|
if (shouldConfigureMcp) {
|
|
3478
3684
|
if (isWindows()) {
|
|
3479
3685
|
console.log(ansis.blue(`\u2139 ${i18n.installation.windowsDetected}`));
|
|
3480
3686
|
}
|
|
3481
|
-
|
|
3482
|
-
if (
|
|
3483
|
-
|
|
3687
|
+
let selectedServices;
|
|
3688
|
+
if (options.skipPrompt) {
|
|
3689
|
+
selectedServices = options.mcpServices;
|
|
3690
|
+
} else {
|
|
3691
|
+
selectedServices = await selectMcpServices(scriptLang);
|
|
3692
|
+
if (selectedServices === void 0) {
|
|
3693
|
+
process.exit(0);
|
|
3694
|
+
}
|
|
3484
3695
|
}
|
|
3485
3696
|
if (selectedServices.length > 0) {
|
|
3486
3697
|
const mcpBackupPath = backupMcpConfig();
|
|
@@ -3493,20 +3704,21 @@ async function init(options = {}) {
|
|
|
3493
3704
|
if (!service) continue;
|
|
3494
3705
|
let config = service.config;
|
|
3495
3706
|
if (service.requiresApiKey) {
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
name: "apiKey",
|
|
3499
|
-
message: service.apiKeyPrompt[scriptLang],
|
|
3500
|
-
validate: (value) => !!value || i18n.api.keyRequired
|
|
3501
|
-
});
|
|
3502
|
-
if (apiKey === void 0) {
|
|
3503
|
-
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
|
|
3707
|
+
if (options.skipPrompt) {
|
|
3708
|
+
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]} (requires API key)`));
|
|
3504
3709
|
continue;
|
|
3505
|
-
}
|
|
3506
|
-
if (apiKey) {
|
|
3507
|
-
config = buildMcpServerConfig(service.config, apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
|
|
3508
3710
|
} else {
|
|
3509
|
-
|
|
3711
|
+
const response = await inquirer.prompt({
|
|
3712
|
+
type: "input",
|
|
3713
|
+
name: "apiKey",
|
|
3714
|
+
message: service.apiKeyPrompt[scriptLang],
|
|
3715
|
+
validate: (value) => !!value || i18n.api.keyRequired
|
|
3716
|
+
});
|
|
3717
|
+
if (!response.apiKey) {
|
|
3718
|
+
console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
|
|
3719
|
+
continue;
|
|
3720
|
+
}
|
|
3721
|
+
config = buildMcpServerConfig(service.config, response.apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
|
|
3510
3722
|
}
|
|
3511
3723
|
}
|
|
3512
3724
|
newServers[service.id] = config;
|
|
@@ -3525,15 +3737,21 @@ async function init(options = {}) {
|
|
|
3525
3737
|
}
|
|
3526
3738
|
const cometixInstalled = await isCometixLineInstalled();
|
|
3527
3739
|
if (!cometixInstalled) {
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3740
|
+
let shouldInstallCometix = false;
|
|
3741
|
+
if (options.skipPrompt) {
|
|
3742
|
+
shouldInstallCometix = options.installCometixLine !== false;
|
|
3743
|
+
} else {
|
|
3744
|
+
const { shouldInstallCometix: userChoice } = await inquirer.prompt({
|
|
3745
|
+
type: "confirm",
|
|
3746
|
+
name: "shouldInstallCometix",
|
|
3747
|
+
message: i18n.cometix.installCometixPrompt,
|
|
3748
|
+
default: true
|
|
3749
|
+
});
|
|
3750
|
+
if (userChoice === void 0) {
|
|
3751
|
+
console.log(ansis.yellow(i18n.common.cancelled));
|
|
3752
|
+
process.exit(0);
|
|
3753
|
+
}
|
|
3754
|
+
shouldInstallCometix = userChoice;
|
|
3537
3755
|
}
|
|
3538
3756
|
if (shouldInstallCometix) {
|
|
3539
3757
|
await installCometixLine(scriptLang);
|