accf
Version:
Claude-Code Flow - One-click configuration tool for Claude Code
1,409 lines (1,399 loc) • 92.4 kB
JavaScript
#!/usr/bin/env node
import cac from 'cac';
import ansis from 'ansis';
import { a5 as ensureI18nInitialized, a0 as i18n, a8 as readCcrConfig, a9 as isCcrInstalled, aa as installCcr, ab as configureCcrFeature, ac as handleExitPromptError, ad as handleGeneralError, p as SUPPORTED_LANGS, a7 as addNumbersToChoices, q as LANG_LABELS, ae as updateZcfConfig, af as changeLanguage, ag as readZcfConfig, o as openSettingsJson, d as importRecommendedPermissions, b as importRecommendedEnv, T as applyAiLanguageDirective, ah as configureOutputStyle, Q as getExistingModelConfig, N as updateCustomModel, O as updateDefaultModel, ai as isWindows, t as readMcpConfig, y as fixWindowsMcpConfig, w as writeMcpConfig, aj as selectMcpServices, u as backupMcpConfig, ak as getMcpServices, x as buildMcpServerConfig, v as mergeMcpServers, al as setupCcrConfiguration, R as getExistingApiConfig, V as promptApiConfigurationAction, am as modifyApiConfigPartially, an as validateApiKey, K as configureApi, ao as formatApiKeyDisplay, U as switchToOfficialLogin, ap as COMETIX_COMMAND_NAME, aq as COMETIX_COMMANDS, ar as installCometixLine, as as checkAndUpdateTools, at as runCodexUpdate, au as resolveCodeType, av as readJsonConfig, aw as writeJsonConfig, j as ZCF_CONFIG_FILE, ax as displayBanner, ay as version, az as resolveAiOutputLanguage, aA as updatePromptOnly, aB as selectAndInstallWorkflows, aC as checkClaudeCodeVersionAndPrompt, n as isCodeToolType, D as DEFAULT_CODE_TOOL_TYPE, aD as displayBannerWithInfo, l as CODE_TOOL_BANNERS, aE as runCodexUninstall, aF as configureCodexMcp, aG as configureCodexApi, aH as runCodexWorkflowImport, aI as runCodexFullInit, i as init, aJ as listCodexProviders, aK as getCurrentCodexProvider, aL as switchCodexProvider, a1 as readCodexConfig, aM as switchToOfficialLogin$1, aN as switchToProvider, aO as readZcfConfigAsync, aP as initI18n, aQ as selectScriptLanguage } from './chunks/simple-config.mjs';
import { existsSync } from 'node:fs';
import { homedir } from 'node:os';
import inquirer from 'inquirer';
import { join } from 'pathe';
import { exec, spawn } from 'node:child_process';
import { promisify } from 'node:util';
import process from 'node:process';
import { x, exec as exec$1 } from 'tinyexec';
import { pathExists } from 'fs-extra';
import { m as moveToTrash } from './shared/accf.DGjQxTq_.mjs';
import 'dayjs';
import 'node:url';
import 'ora';
import 'semver';
import 'smol-toml';
import 'node:fs/promises';
import 'i18next';
import 'i18next-fs-backend';
import 'trash';
const execAsync$1 = promisify(exec);
async function runCcrUi(apiKey) {
ensureI18nInitialized();
console.log(ansis.cyan(`
\u{1F5A5}\uFE0F ${i18n.t("ccr:startingCcrUi")}`));
if (apiKey) {
console.log(ansis.bold.green(`
\u{1F511} ${i18n.t("ccr:ccrUiApiKey") || "CCR UI API Key"}: ${apiKey}`));
console.log(ansis.gray(` ${i18n.t("ccr:ccrUiApiKeyHint") || "Use this API key to login to CCR UI"}
`));
}
try {
const { stdout, stderr } = await execAsync$1("ccr ui");
if (stdout)
console.log(stdout);
if (stderr)
console.error(ansis.yellow(stderr));
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrUiStarted")}`));
} catch (error) {
console.error(ansis.red(`\u2716 ${i18n.t("ccr:ccrCommandFailed")}: ${error instanceof Error ? error.message : String(error)}`));
throw error;
}
}
async function runCcrStatus() {
ensureI18nInitialized();
console.log(ansis.cyan(`
\u{1F4CA} ${i18n.t("ccr:checkingCcrStatus")}`));
try {
const { stdout, stderr } = await execAsync$1("ccr status");
if (stdout) {
console.log(`
${ansis.bold(i18n.t("ccr:ccrStatusTitle"))}`);
console.log(stdout);
}
if (stderr)
console.error(ansis.yellow(stderr));
} catch (error) {
console.error(ansis.red(`\u2716 ${i18n.t("ccr:ccrCommandFailed")}: ${error instanceof Error ? error.message : String(error)}`));
throw error;
}
}
async function runCcrRestart() {
ensureI18nInitialized();
console.log(ansis.cyan(`
\u{1F504} ${i18n.t("ccr:restartingCcr")}`));
try {
const { stdout, stderr } = await execAsync$1("ccr restart");
if (stdout)
console.log(stdout);
if (stderr)
console.error(ansis.yellow(stderr));
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrRestarted")}`));
} catch (error) {
console.error(ansis.red(`\u2716 ${i18n.t("ccr:ccrCommandFailed")}: ${error instanceof Error ? error.message : String(error)}`));
throw error;
}
}
async function runCcrStart() {
ensureI18nInitialized();
console.log(ansis.cyan(`
\u25B6\uFE0F ${i18n.t("ccr:startingCcr")}`));
try {
const { stdout, stderr } = await execAsync$1("ccr start");
if (stdout)
console.log(stdout);
if (stderr)
console.error(ansis.yellow(stderr));
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrStarted")}`));
} catch (error) {
if (error.stdout && error.stdout.includes("Loaded JSON config from:")) {
console.log(error.stdout);
if (error.stderr)
console.error(ansis.yellow(error.stderr));
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrStarted")}`));
} else {
console.error(ansis.red(`\u2716 ${i18n.t("ccr:ccrCommandFailed")}: ${error instanceof Error ? error.message : String(error)}`));
throw error;
}
}
}
async function runCcrStop() {
ensureI18nInitialized();
console.log(ansis.cyan(`
\u23F9\uFE0F ${i18n.t("ccr:stoppingCcr")}`));
try {
const { stdout, stderr } = await execAsync$1("ccr stop");
if (stdout)
console.log(stdout);
if (stderr)
console.error(ansis.yellow(stderr));
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrStopped")}`));
} catch (error) {
console.error(ansis.red(`\u2716 ${i18n.t("ccr:ccrCommandFailed")}: ${error instanceof Error ? error.message : String(error)}`));
throw error;
}
}
function isCcrConfigured() {
const CCR_CONFIG_FILE = join(homedir(), ".claude-code-router", "config.json");
if (!existsSync(CCR_CONFIG_FILE)) {
return false;
}
const config = readCcrConfig();
return config !== null;
}
async function showCcrMenu() {
try {
ensureI18nInitialized();
console.log(`
${ansis.cyan("\u2550".repeat(50))}`);
console.log(ansis.bold.cyan(` ${i18n.t("ccr:ccrMenuTitle")}`));
console.log(`${ansis.cyan("\u2550".repeat(50))}
`);
console.log(` ${ansis.cyan("1.")} ${i18n.t("ccr:ccrMenuOptions.initCcr")} ${ansis.gray(`- ${i18n.t("ccr:ccrMenuDescriptions.initCcr")}`)}`);
console.log(` ${ansis.cyan("2.")} ${i18n.t("ccr:ccrMenuOptions.startUi")} ${ansis.gray(`- ${i18n.t("ccr:ccrMenuDescriptions.startUi")}`)}`);
console.log(` ${ansis.cyan("3.")} ${i18n.t("ccr:ccrMenuOptions.checkStatus")} ${ansis.gray(`- ${i18n.t("ccr:ccrMenuDescriptions.checkStatus")}`)}`);
console.log(` ${ansis.cyan("4.")} ${i18n.t("ccr:ccrMenuOptions.restart")} ${ansis.gray(`- ${i18n.t("ccr:ccrMenuDescriptions.restart")}`)}`);
console.log(` ${ansis.cyan("5.")} ${i18n.t("ccr:ccrMenuOptions.start")} ${ansis.gray(`- ${i18n.t("ccr:ccrMenuDescriptions.start")}`)}`);
console.log(` ${ansis.cyan("6.")} ${i18n.t("ccr:ccrMenuOptions.stop")} ${ansis.gray(`- ${i18n.t("ccr:ccrMenuDescriptions.stop")}`)}`);
console.log(` ${ansis.yellow("0.")} ${i18n.t("ccr:ccrMenuOptions.back")}`);
console.log("");
const { choice } = await inquirer.prompt({
type: "input",
name: "choice",
message: i18n.t("common:enterChoice"),
validate: (value) => {
const valid = ["1", "2", "3", "4", "5", "6", "0"];
return valid.includes(value) || i18n.t("common:invalidChoice");
}
});
switch (choice) {
case "1": {
const ccrStatus = await isCcrInstalled();
if (!ccrStatus.hasCorrectPackage) {
await installCcr();
} else {
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrAlreadyInstalled")}`));
}
await configureCcrFeature();
console.log(ansis.green(`
\u2714 ${i18n.t("ccr:ccrSetupComplete")}`));
break;
}
case "2":
if (!isCcrConfigured()) {
console.log(ansis.yellow(`
\u26A0\uFE0F ${i18n.t("ccr:ccrNotConfigured")}`));
console.log(ansis.cyan(` ${i18n.t("ccr:pleaseInitFirst")}
`));
} else {
const config = readCcrConfig();
await runCcrUi(config?.APIKEY);
}
break;
case "3":
if (!isCcrConfigured()) {
console.log(ansis.yellow(`
\u26A0\uFE0F ${i18n.t("ccr:ccrNotConfigured")}`));
console.log(ansis.cyan(` ${i18n.t("ccr:pleaseInitFirst")}
`));
} else {
await runCcrStatus();
}
break;
case "4":
if (!isCcrConfigured()) {
console.log(ansis.yellow(`
\u26A0\uFE0F ${i18n.t("ccr:ccrNotConfigured")}`));
console.log(ansis.cyan(` ${i18n.t("ccr:pleaseInitFirst")}
`));
} else {
await runCcrRestart();
}
break;
case "5":
if (!isCcrConfigured()) {
console.log(ansis.yellow(`
\u26A0\uFE0F ${i18n.t("ccr:ccrNotConfigured")}`));
console.log(ansis.cyan(` ${i18n.t("ccr:pleaseInitFirst")}
`));
} else {
await runCcrStart();
}
break;
case "6":
if (!isCcrConfigured()) {
console.log(ansis.yellow(`
\u26A0\uFE0F ${i18n.t("ccr:ccrNotConfigured")}`));
console.log(ansis.cyan(` ${i18n.t("ccr:pleaseInitFirst")}
`));
} else {
await runCcrStop();
}
break;
case "0":
return false;
}
if (choice !== "0") {
console.log(`
${ansis.dim("\u2500".repeat(50))}
`);
const { continueInCcr } = await inquirer.prompt({
type: "confirm",
name: "continueInCcr",
message: i18n.t("common:returnToMenu"),
default: true
});
if (continueInCcr) {
return await showCcrMenu();
}
}
return false;
} catch (error) {
if (!handleExitPromptError(error)) {
handleGeneralError(error);
}
return false;
}
}
async function handleCancellation() {
ensureI18nInitialized();
console.log(ansis.yellow(i18n.t("common:cancelled")));
}
async function handleOfficialLoginMode() {
ensureI18nInitialized();
const success = switchToOfficialLogin();
if (success) {
console.log(ansis.green(`\u2714 ${i18n.t("api:officialLoginConfigured")}`));
} else {
console.log(ansis.red(i18n.t("api:officialLoginFailed")));
}
}
async function handleCustomApiMode() {
ensureI18nInitialized();
const existingConfig = getExistingApiConfig();
if (existingConfig) {
const configAction = await promptApiConfigurationAction();
if (configAction === "keep-existing") {
console.log(ansis.green(`\u2714 ${i18n.t("api:keepExistingConfig")}`));
return;
} else if (configAction === "modify-partial") {
await modifyApiConfigPartially(existingConfig);
return;
}
}
const { apiChoice } = await inquirer.prompt({
type: "list",
name: "apiChoice",
message: i18n.t("api:configureApi"),
choices: addNumbersToChoices([
{
name: `${i18n.t("api:useAuthToken")} - ${ansis.gray(i18n.t("api:authTokenDesc"))}`,
value: "auth_token",
short: i18n.t("api:useAuthToken")
},
{
name: `${i18n.t("api:useApiKey")} - ${ansis.gray(i18n.t("api:apiKeyDesc"))}`,
value: "api_key",
short: i18n.t("api:useApiKey")
},
{ name: i18n.t("api:skipApi"), value: "skip" }
])
});
if (!apiChoice || apiChoice === "skip") {
await handleCancellation();
return;
}
const { url } = await inquirer.prompt({
type: "input",
name: "url",
message: `${i18n.t("api:enterApiUrl")}${i18n.t("common:emptyToSkip")}`,
validate: (value) => {
if (!value) {
return true;
}
try {
void new URL(value);
return true;
} catch {
return i18n.t("api:invalidUrl");
}
}
});
if (url === void 0 || !url) {
await handleCancellation();
return;
}
const keyMessage = apiChoice === "auth_token" ? `${i18n.t("api:enterAuthToken")}${i18n.t("common:emptyToSkip")}` : `${i18n.t("api:enterApiKey")}${i18n.t("common:emptyToSkip")}`;
const { key } = await inquirer.prompt({
type: "input",
name: "key",
message: keyMessage,
validate: (value) => {
if (!value) {
return true;
}
const validation = validateApiKey(value);
if (!validation.isValid) {
return validation.error || i18n.t("api:invalidKeyFormat");
}
return true;
}
});
if (key === void 0 || !key) {
await handleCancellation();
return;
}
const apiConfig = { url, key, authType: apiChoice };
const configuredApi = configureApi(apiConfig);
if (configuredApi) {
console.log(ansis.green(`\u2714 ${i18n.t("api:apiConfigSuccess")}`));
console.log(ansis.gray(` URL: ${configuredApi.url}`));
console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
}
}
async function handleCcrProxyMode() {
ensureI18nInitialized();
const ccrStatus = await isCcrInstalled();
if (!ccrStatus.hasCorrectPackage) {
await installCcr();
} else {
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrAlreadyInstalled")}`));
}
const ccrConfigured = await setupCcrConfiguration();
if (ccrConfigured) {
console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrSetupComplete")}`));
}
}
async function configureApiFeature() {
ensureI18nInitialized();
const { mode } = await inquirer.prompt({
type: "list",
name: "mode",
message: i18n.t("api:apiModePrompt"),
choices: addNumbersToChoices([
{ name: i18n.t("api:apiModeOfficial"), value: "official" },
{ name: i18n.t("api:apiModeCustom"), value: "custom" },
{ name: i18n.t("api:apiModeCcr"), value: "ccr" },
{ name: i18n.t("api:apiModeSkip"), value: "skip" }
])
});
if (!mode || mode === "skip") {
await handleCancellation();
return;
}
switch (mode) {
case "official":
await handleOfficialLoginMode();
break;
case "custom":
await handleCustomApiMode();
break;
case "ccr":
await handleCcrProxyMode();
break;
default:
await handleCancellation();
break;
}
}
async function configureMcpFeature() {
ensureI18nInitialized();
if (isWindows()) {
const { fixWindows } = await inquirer.prompt({
type: "confirm",
name: "fixWindows",
message: i18n.t("configuration:fixWindowsMcp") || "Fix Windows MCP configuration?",
default: true
});
if (fixWindows) {
const existingConfig = readMcpConfig() || { mcpServers: {} };
const fixedConfig = fixWindowsMcpConfig(existingConfig);
writeMcpConfig(fixedConfig);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:windowsMcpConfigFixed")}`));
}
}
const selectedServices = await selectMcpServices();
if (!selectedServices) {
return;
}
if (selectedServices.length > 0) {
const mcpBackupPath = backupMcpConfig();
if (mcpBackupPath) {
console.log(ansis.gray(`\u2714 ${i18n.t("mcp:mcpBackupSuccess")}: ${mcpBackupPath}`));
}
const newServers = {};
for (const serviceId of selectedServices) {
const service = (await getMcpServices()).find((s) => s.id === serviceId);
if (!service)
continue;
let config = service.config;
if (service.requiresApiKey) {
const { apiKey } = await inquirer.prompt({
type: "password",
name: "apiKey",
message: service.apiKeyPrompt + i18n.t("common:inputHidden"),
validate: async (value) => !!value || i18n.t("api:keyRequired")
});
if (apiKey) {
config = buildMcpServerConfig(service.config, apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
} else {
continue;
}
}
newServers[service.id] = config;
}
const existingConfig = readMcpConfig();
let mergedConfig = mergeMcpServers(existingConfig, newServers);
mergedConfig = fixWindowsMcpConfig(mergedConfig);
writeMcpConfig(mergedConfig);
console.log(ansis.green(`\u2714 ${i18n.t("mcp:mcpConfigSuccess")}`));
}
}
async function configureDefaultModelFeature() {
ensureI18nInitialized();
const existingModel = getExistingModelConfig();
if (existingModel) {
console.log(`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingModelConfig") || "Existing model configuration"}`)}`);
const modelDisplay = existingModel === "default" ? i18n.t("configuration:defaultModelOption") || "Default (Let Claude Code choose)" : existingModel.charAt(0).toUpperCase() + existingModel.slice(1);
console.log(ansis.gray(` ${i18n.t("configuration:currentModel") || "Current model"}: ${modelDisplay}
`));
const { modify } = await inquirer.prompt({
type: "confirm",
name: "modify",
message: i18n.t("configuration:modifyModel") || "Modify model configuration?",
default: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepModel") || "Keeping existing model configuration"}`));
return;
}
}
const { model } = await inquirer.prompt({
type: "list",
name: "model",
message: i18n.t("configuration:selectDefaultModel") || "Select default model",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:defaultModelOption") || "Default - Let Claude Code choose",
value: "default"
},
{
name: i18n.t("configuration:opusModelOption") || "Opus - Only use opus, high token consumption, use with caution",
value: "opus"
},
{
name: i18n.t("configuration:sonnet1mModelOption") || "Sonnet 1M - 1M context version",
value: "sonnet[1m]"
},
{
name: i18n.t("configuration:customModelOption") || "Custom - Specify custom model names",
value: "custom"
}
]),
default: existingModel ? ["default", "opus", "sonnet[1m]", "custom"].indexOf(existingModel) : 0
});
if (!model) {
await handleCancellation();
return;
}
if (model === "custom") {
const { primaryModel, fastModel } = await promptCustomModels();
if (!primaryModel.trim() && !fastModel.trim()) {
console.log(ansis.yellow(`\u26A0 ${i18n.t("configuration:customModelSkipped") || "Custom model configuration skipped"}`));
return;
}
updateCustomModel(primaryModel, fastModel);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:customModelConfigured") || "Custom model configuration completed"}`));
return;
}
updateDefaultModel(model);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:modelConfigured") || "Default model configured"}`));
}
async function promptCustomModels() {
const { primaryModel } = await inquirer.prompt({
type: "input",
name: "primaryModel",
message: `${i18n.t("configuration:enterPrimaryModel")}${i18n.t("common:emptyToSkip")}`,
default: ""
});
const { fastModel } = await inquirer.prompt({
type: "input",
name: "fastModel",
message: `${i18n.t("configuration:enterFastModel")}${i18n.t("common:emptyToSkip")}`,
default: ""
});
return { primaryModel, fastModel };
}
async function configureAiMemoryFeature() {
ensureI18nInitialized();
const { option } = await inquirer.prompt({
type: "list",
name: "option",
message: i18n.t("configuration:selectMemoryOption") || "Select configuration option",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:configureAiLanguage") || "Configure AI output language",
value: "language"
},
{
name: i18n.t("configuration:configureOutputStyle") || "Configure global AI output style",
value: "outputStyle"
}
])
});
if (!option) {
return;
}
if (option === "language") {
const zcfConfig = readZcfConfig();
const existingLang = zcfConfig?.aiOutputLang;
if (existingLang) {
console.log(
`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingLanguageConfig") || "Existing AI output language configuration"}`)}`
);
console.log(ansis.gray(` ${i18n.t("configuration:currentLanguage") || "Current language"}: ${existingLang}
`));
const { modify } = await inquirer.prompt({
type: "confirm",
name: "modify",
message: i18n.t("configuration:modifyLanguage") || "Modify AI output language?",
default: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepLanguage") || "Keeping existing language configuration"}`));
return;
}
}
const { selectAiOutputLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aS; });
const aiOutputLang = await selectAiOutputLanguage();
applyAiLanguageDirective(aiOutputLang);
updateZcfConfig({ aiOutputLang });
console.log(ansis.green(`\u2714 ${i18n.t("configuration:aiLanguageConfigured") || "AI output language configured"}`));
} else if (option === "outputStyle") {
await configureOutputStyle();
}
}
async function changeScriptLanguageFeature(currentLang) {
ensureI18nInitialized();
const { lang } = await inquirer.prompt({
type: "list",
name: "lang",
message: i18n.t("language:selectScriptLang"),
choices: addNumbersToChoices(
SUPPORTED_LANGS.map((l) => ({
name: LANG_LABELS[l],
value: l
}))
),
default: SUPPORTED_LANGS.indexOf(currentLang)
});
if (!lang) {
return currentLang;
}
updateZcfConfig({ preferredLang: lang });
await changeLanguage(lang);
console.log(ansis.green(`\u2714 ${i18n.t("language:languageChanged") || "Language changed"}`));
return lang;
}
async function configureCodexDefaultModelFeature() {
ensureI18nInitialized();
const { readCodexConfig } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aT; });
const existingConfig = readCodexConfig();
const currentModel = existingConfig?.model;
if (currentModel) {
console.log(`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingModelConfig") || "Existing model configuration"}`)}`);
const modelDisplay = currentModel === "gpt-5-codex" ? "GPT-5-Codex" : currentModel === "gpt-5" ? "GPT-5" : currentModel.charAt(0).toUpperCase() + currentModel.slice(1);
console.log(ansis.gray(` ${i18n.t("configuration:currentModel") || "Current model"}: ${modelDisplay}
`));
const { modify } = await inquirer.prompt({
type: "confirm",
name: "modify",
message: i18n.t("configuration:modifyModel") || "Modify model configuration?",
default: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepModel") || "Keeping existing model configuration"}`));
return;
}
}
const { model } = await inquirer.prompt({
type: "list",
name: "model",
message: i18n.t("configuration:selectDefaultModel") || "Select default model",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:codexModelOptions.gpt5"),
value: "gpt-5"
},
{
name: i18n.t("configuration:codexModelOptions.gpt5Codex"),
value: "gpt-5-codex"
},
{
name: i18n.t("configuration:codexModelOptions.custom"),
value: "custom"
}
]),
default: currentModel ? ["gpt-5", "gpt-5-codex", "custom"].indexOf(currentModel) : 1
// Default to gpt-5-codex
});
if (!model) {
await handleCancellation();
return;
}
if (model === "custom") {
const { customModel } = await inquirer.prompt({
type: "input",
name: "customModel",
message: `${i18n.t("configuration:enterCustomModel")}${i18n.t("common:emptyToSkip")}`,
default: ""
});
if (!customModel.trim()) {
console.log(ansis.yellow(`\u26A0 ${i18n.t("configuration:customModelSkipped") || "Custom model configuration skipped"}`));
return;
}
await updateCodexModelProvider(customModel.trim());
console.log(ansis.green(`\u2714 ${i18n.t("configuration:customModelConfigured") || "Custom model configuration completed"}`));
return;
}
await updateCodexModelProvider(model);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:modelConfigured") || "Default model configured"}`));
}
async function configureCodexAiMemoryFeature() {
ensureI18nInitialized();
const { option } = await inquirer.prompt({
type: "list",
name: "option",
message: i18n.t("configuration:selectMemoryOption") || "Select configuration option",
choices: addNumbersToChoices([
{
name: i18n.t("configuration:configureAiLanguage") || "Configure AI output language",
value: "language"
},
{
name: i18n.t("configuration:configureSystemPromptStyle") || "Configure global AI system prompt style",
value: "systemPrompt"
}
])
});
if (!option) {
return;
}
if (option === "language") {
const zcfConfig = readZcfConfig();
const existingLang = zcfConfig?.aiOutputLang;
if (existingLang) {
console.log(
`
${ansis.blue(`\u2139 ${i18n.t("configuration:existingLanguageConfig") || "Existing AI output language configuration"}`)}`
);
console.log(ansis.gray(` ${i18n.t("configuration:currentLanguage") || "Current language"}: ${existingLang}
`));
const { modify } = await inquirer.prompt({
type: "confirm",
name: "modify",
message: i18n.t("configuration:modifyLanguage") || "Modify AI output language?",
default: false
});
if (!modify) {
console.log(ansis.green(`\u2714 ${i18n.t("configuration:keepLanguage") || "Keeping existing language configuration"}`));
await ensureLanguageDirectiveInAgents(existingLang);
return;
}
}
const { selectAiOutputLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aS; });
const aiOutputLang = await selectAiOutputLanguage();
await updateCodexLanguageDirective(aiOutputLang);
updateZcfConfig({ aiOutputLang });
console.log(ansis.green(`\u2714 ${i18n.t("configuration:aiLanguageConfigured") || "AI output language configured"}`));
} else if (option === "systemPrompt") {
const zcfConfig = readZcfConfig();
const currentLang = zcfConfig?.aiOutputLang || "English";
const { runCodexSystemPromptSelection } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aT; });
await runCodexSystemPromptSelection();
await ensureLanguageDirectiveInAgents(currentLang);
console.log(ansis.green(`\u2714 ${i18n.t("configuration:systemPromptConfigured")}`));
}
}
async function updateCodexModelProvider(modelProvider) {
const { readCodexConfig, writeCodexConfig, backupCodexConfig, getBackupMessage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aT; });
const backupPath = backupCodexConfig();
if (backupPath) {
console.log(ansis.gray(getBackupMessage(backupPath)));
}
const existingConfig = readCodexConfig();
const updatedConfig = {
...existingConfig,
model: modelProvider,
// Set the model field
modelProvider: existingConfig?.modelProvider || null,
// Preserve existing API provider
providers: existingConfig?.providers || [],
mcpServices: existingConfig?.mcpServices || [],
managed: true,
otherConfig: existingConfig?.otherConfig || [],
modelProviderCommented: existingConfig?.modelProviderCommented
};
writeCodexConfig(updatedConfig);
}
async function ensureLanguageDirectiveInAgents(aiOutputLang) {
const { readFile, writeFile, exists } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aR; });
const { homedir } = await import('node:os');
const { join } = await import('pathe');
const CODEX_AGENTS_FILE = join(homedir(), ".codex", "AGENTS.md");
if (!exists(CODEX_AGENTS_FILE)) {
console.log(ansis.yellow(i18n.t("codex:agentsFileNotFound")));
return;
}
const content = readFile(CODEX_AGENTS_FILE);
const languageLabels = {
"Chinese": "Chinese-simplified",
"English": "English",
"zh-CN": "Chinese-simplified",
"en": "English"
};
const langLabel = languageLabels[aiOutputLang] || aiOutputLang;
const hasLanguageDirective = /\*\*Most Important:\s*Always respond in [^*]+\*\*/i.test(content);
if (!hasLanguageDirective) {
const { backupCodexAgents, getBackupMessage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aT; });
const backupPath = backupCodexAgents();
if (backupPath) {
console.log(ansis.gray(getBackupMessage(backupPath)));
}
let updatedContent = content;
if (!updatedContent.endsWith("\n")) {
updatedContent += "\n";
}
updatedContent += `
**Most Important:Always respond in ${langLabel}**
`;
writeFile(CODEX_AGENTS_FILE, updatedContent);
console.log(ansis.gray(` ${i18n.t("configuration:addedLanguageDirective")}: ${langLabel}`));
}
}
async function updateCodexLanguageDirective(aiOutputLang) {
const { readFile, writeFile, exists } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aR; });
const { backupCodexAgents, getBackupMessage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.aT; });
const { homedir } = await import('node:os');
const { join } = await import('pathe');
const CODEX_AGENTS_FILE = join(homedir(), ".codex", "AGENTS.md");
if (!exists(CODEX_AGENTS_FILE)) {
console.log(ansis.yellow(i18n.t("codex:agentsFileNotFound")));
return;
}
const backupPath = backupCodexAgents();
if (backupPath) {
console.log(ansis.gray(getBackupMessage(backupPath)));
}
let content = readFile(CODEX_AGENTS_FILE);
const languageLabels = {
"Chinese": "Chinese-simplified",
"English": "English",
"zh-CN": "Chinese-simplified",
"en": "English"
};
const langLabel = languageLabels[aiOutputLang] || aiOutputLang;
content = content.replace(/\*\*Most Important:\s*Always respond in [^*]+\*\*\s*/g, "");
if (!content.endsWith("\n")) {
content += "\n";
}
content += `
**Most Important:Always respond in ${langLabel}**
`;
writeFile(CODEX_AGENTS_FILE, content);
}
async function configureEnvPermissionFeature() {
ensureI18nInitialized();
const { choice } = await inquirer.prompt({
type: "list",
name: "choice",
message: i18n.t("configuration:selectEnvPermissionOption") || "Select option",
choices: addNumbersToChoices([
{
name: `${i18n.t("configuration:importRecommendedEnv") || "Import environment"} ${ansis.gray(
`- ${i18n.t("configuration:importRecommendedEnvDesc") || "Import env settings"}`
)}`,
value: "env"
},
{
name: `${i18n.t("configuration:importRecommendedPermissions") || "Import permissions"} ${ansis.gray(
`- ${i18n.t("configuration:importRecommendedPermissionsDesc") || "Import permission settings"}`
)}`,
value: "permissions"
},
{
name: `${i18n.t("configuration:openSettingsJson") || "Open settings"} ${ansis.gray(
`- ${i18n.t("configuration:openSettingsJsonDesc") || "View settings file"}`
)}`,
value: "open"
}
])
});
if (!choice) {
await handleCancellation();
return;
}
try {
switch (choice) {
case "env":
await importRecommendedEnv();
console.log(ansis.green(`\u2705 ${i18n.t("configuration:envImportSuccess")}`));
break;
case "permissions":
await importRecommendedPermissions();
console.log(ansis.green(`\u2705 ${i18n.t("configuration:permissionsImportSuccess") || "Permissions imported"}`));
break;
case "open":
console.log(ansis.cyan(i18n.t("configuration:openingSettingsJson") || "Opening settings.json..."));
await openSettingsJson();
break;
}
} catch (error) {
console.error(ansis.red(`${i18n.t("common:error")}: ${error.message}`));
}
}
async function executeCcusage(args = []) {
try {
const command = "npx";
const commandArgs = ["ccusage@latest", ...args || []];
console.log(ansis.cyan(i18n.t("tools:runningCcusage")));
console.log(ansis.gray(`$ npx ccusage@latest ${(args || []).join(" ")}`));
console.log("");
await x(command, commandArgs, {
nodeOptions: {
stdio: "inherit"
}
});
} catch (error) {
console.error(ansis.red(i18n.t("tools:ccusageFailed")));
console.error(ansis.yellow(i18n.t("tools:checkNetworkConnection")));
if (process.env.DEBUG) {
console.error(ansis.gray(i18n.t("tools:errorDetails")), error);
}
if (process.env.NODE_ENV !== "test") {
process.exit(1);
}
throw error;
}
}
const execAsync = promisify(exec);
async function runCometixPrintConfig() {
ensureI18nInitialized();
try {
console.log(ansis.blue(`${i18n.t("cometix:printingConfig")}`));
const { stdout } = await execAsync(COMETIX_COMMANDS.PRINT_CONFIG);
console.log(stdout);
} catch (error) {
if (error.message.includes(`command not found: ${COMETIX_COMMAND_NAME}`)) {
console.error(ansis.red(`\u2717 ${i18n.t("cometix:commandNotFound")}`));
} else {
console.error(ansis.red(`\u2717 ${i18n.t("cometix:printConfigFailed")}: ${error}`));
}
throw error;
}
}
async function runCometixTuiConfig() {
ensureI18nInitialized();
return new Promise((resolve, reject) => {
console.log(ansis.blue(`${i18n.t("cometix:enteringTuiConfig")}`));
const child = spawn(COMETIX_COMMAND_NAME, ["-c"], {
stdio: "inherit",
// This allows the TUI to interact directly with the terminal
shell: true
});
child.on("close", (code) => {
if (code === 0) {
console.log(ansis.green(`\u2713 ${i18n.t("cometix:tuiConfigSuccess")}`));
resolve();
} else {
const error = new Error(`${COMETIX_COMMAND_NAME} -c exited with code ${code}`);
console.error(ansis.red(`\u2717 ${i18n.t("cometix:tuiConfigFailed")}: ${error.message}`));
reject(error);
}
});
child.on("error", (error) => {
if (error.message.includes(`command not found`) || error.message.includes("ENOENT")) {
console.error(ansis.red(`\u2717 ${i18n.t("cometix:commandNotFound")}`));
} else {
console.error(ansis.red(`\u2717 ${i18n.t("cometix:tuiConfigFailed")}: ${error.message}`));
}
reject(error);
});
});
}
async function showCometixMenu() {
try {
ensureI18nInitialized();
console.log(`
${ansis.cyan("\u2550".repeat(50))}`);
console.log(ansis.bold.cyan(` ${i18n.t("cometix:cometixMenuTitle")}`));
console.log(`${ansis.cyan("\u2550".repeat(50))}
`);
console.log(` ${ansis.cyan("1.")} ${i18n.t("cometix:cometixMenuOptions.installOrUpdate")} ${ansis.gray(`- ${i18n.t("cometix:cometixMenuDescriptions.installOrUpdate")}`)}`);
console.log(` ${ansis.cyan("2.")} ${i18n.t("cometix:cometixMenuOptions.printConfig")} ${ansis.gray(`- ${i18n.t("cometix:cometixMenuDescriptions.printConfig")}`)}`);
console.log(` ${ansis.cyan("3.")} ${i18n.t("cometix:cometixMenuOptions.customConfig")} ${ansis.gray(`- ${i18n.t("cometix:cometixMenuDescriptions.customConfig")}`)}`);
console.log(` ${ansis.yellow("0.")} ${i18n.t("cometix:cometixMenuOptions.back")}`);
console.log("");
const { choice } = await inquirer.prompt({
type: "input",
name: "choice",
message: i18n.t("common:enterChoice"),
validate: async (value) => {
const valid = ["1", "2", "3", "0"];
return valid.includes(value) || i18n.t("common:invalidChoice");
}
});
switch (choice) {
case "1":
await installCometixLine();
break;
case "2":
await runCometixPrintConfig();
break;
case "3":
await runCometixTuiConfig();
break;
case "0":
return false;
}
if (choice !== "0") {
console.log(`
${ansis.dim("\u2500".repeat(50))}
`);
const { continueInCometix } = await inquirer.prompt({
type: "confirm",
name: "continueInCometix",
message: i18n.t("common:returnToMenu"),
default: true
});
if (continueInCometix) {
return await showCometixMenu();
}
}
return false;
} catch (error) {
if (!handleExitPromptError(error)) {
handleGeneralError(error);
}
return false;
}
}
async function runCcusageFeature() {
ensureI18nInitialized();
console.log("");
console.log(ansis.cyan(i18n.t("menu:menuOptions.ccusage")));
console.log(ansis.gray(`${i18n.t("tools:ccusageDescription")}`));
console.log("");
const choices = [
{ name: i18n.t("tools:ccusageModes.daily"), value: "daily" },
{ name: i18n.t("tools:ccusageModes.monthly"), value: "monthly" },
{ name: i18n.t("tools:ccusageModes.session"), value: "session" },
{ name: i18n.t("tools:ccusageModes.blocks"), value: "blocks" },
{ name: i18n.t("tools:ccusageModes.custom"), value: "custom" },
{ name: i18n.t("common:back"), value: "back" }
];
const { mode } = await inquirer.prompt({
type: "list",
name: "mode",
message: i18n.t("tools:selectAnalysisMode"),
choices: addNumbersToChoices(choices)
});
if (mode === "back") {
return;
}
let args = [];
if (mode === "custom") {
const { customArgs } = await inquirer.prompt({
type: "input",
name: "customArgs",
message: i18n.t("tools:enterCustomArgs"),
default: ""
});
if (customArgs === null || customArgs === void 0 || customArgs === "") {
args = [];
} else {
const argsString = String(customArgs).trim();
if (!argsString) {
args = [];
} else {
const argPattern = /"([^"]*)"|'([^']*)'|(\S+)/g;
const matches = [];
let match = argPattern.exec(argsString);
while (match !== null) {
const value = match[1] || match[2] || match[3];
if (value) {
matches.push(value);
}
match = argPattern.exec(argsString);
}
args = matches;
}
}
} else {
args = [mode];
}
console.log("");
await executeCcusage(args);
console.log("");
await inquirer.prompt({
type: "input",
name: "continue",
message: ansis.gray(i18n.t("tools:pressEnterToContinue"))
});
}
async function runCcrMenuFeature() {
await showCcrMenu();
}
async function runCometixMenuFeature() {
await showCometixMenu();
}
class ToolUpdateScheduler {
/**
* Update tools based on code type
* @param codeType - The code tool type to update
* @param skipPrompt - Whether to skip interactive prompts
*/
async updateByCodeType(codeType, skipPrompt = false) {
await ensureI18nInitialized();
switch (codeType) {
case "claude-code":
await this.updateClaudeCodeTools(skipPrompt);
break;
case "codex":
await this.updateCodexTools(skipPrompt);
break;
default:
throw new Error(`Unsupported code type: ${codeType}`);
}
}
/**
* Update Claude Code related tools
* @param skipPrompt - Whether to skip interactive prompts
*/
async updateClaudeCodeTools(skipPrompt) {
await checkAndUpdateTools(skipPrompt);
}
/**
* Update Codex tools
* @param skipPrompt - Whether to skip interactive prompts
*/
async updateCodexTools(skipPrompt) {
await runCodexUpdate(false, skipPrompt);
}
}
async function checkUpdates(options = {}) {
try {
const skipPrompt = options.skipPrompt || false;
let codeType;
try {
codeType = await resolveCodeType(options.codeType);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
console.error(ansis.red(`${errorMessage}
Defaulting to "claude-code".`));
codeType = "claude-code";
}
const scheduler = new ToolUpdateScheduler();
await scheduler.updateByCodeType(codeType, skipPrompt);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(ansis.red(`${i18n.t("updater:errorCheckingUpdates")} ${errorMessage}`));
process.exit(1);
}
}
class ZcfUninstaller {
_lang;
// Reserved for future i18n support
conflictResolution = /* @__PURE__ */ new Map();
constructor(lang = "en") {
this._lang = lang;
this.conflictResolution.set("claude-code", ["mcps"]);
void this._lang;
}
/**
* 1. Remove outputStyle field from settings.json and output-styles directory
*/
async removeOutputStyles() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const settingsPath = join(homedir(), ".claude", "settings.json");
const outputStylesPath = join(homedir(), ".claude", "output-styles");
if (await pathExists(settingsPath)) {
const settings = readJsonConfig(settingsPath) || {};
if (settings.outputStyle) {
delete settings.outputStyle;
writeJsonConfig(settingsPath, settings);
result.removedConfigs.push("outputStyle field from settings.json");
}
} else {
result.warnings.push(i18n.t("uninstall:settingsJsonNotFound"));
}
if (await pathExists(outputStylesPath)) {
const trashResult = await moveToTrash(outputStylesPath);
if (!trashResult[0]?.success) {
result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
}
result.removed.push("~/.claude/output-styles/");
} else {
result.warnings.push(i18n.t("uninstall:outputStylesDirectoryNotFound"));
}
result.success = true;
} catch (error) {
result.errors.push(`Failed to remove output styles: ${error.message}`);
}
return result;
}
/**
* 2. Remove custom commands directory (commands/zcf/)
*/
async removeCustomCommands() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const commandsPath = join(homedir(), ".claude", "commands", "zcf");
if (await pathExists(commandsPath)) {
const trashResult = await moveToTrash(commandsPath);
if (!trashResult[0]?.success) {
result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
}
result.removed.push("commands/zcf/");
result.success = true;
} else {
result.warnings.push(i18n.t("uninstall:commandsNotFound"));
result.success = true;
}
} catch (error) {
result.errors.push(`Failed to remove custom commands: ${error.message}`);
}
return result;
}
/**
* 3. Remove custom agents directory (agents/zcf/)
*/
async removeCustomAgents() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const agentsPath = join(homedir(), ".claude", "agents", "zcf");
if (await pathExists(agentsPath)) {
const trashResult = await moveToTrash(agentsPath);
if (!trashResult[0]?.success) {
result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
}
result.removed.push("agents/zcf/");
result.success = true;
} else {
result.warnings.push(i18n.t("uninstall:agentsNotFound"));
result.success = true;
}
} catch (error) {
result.errors.push(`Failed to remove custom agents: ${error.message}`);
}
return result;
}
/**
* 4. Remove global memory file (CLAUDE.md)
*/
async removeClaudeMd() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
if (await pathExists(claudeMdPath)) {
const trashResult = await moveToTrash(claudeMdPath);
if (!trashResult[0]?.success) {
result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
}
result.removed.push("CLAUDE.md");
result.success = true;
} else {
result.warnings.push(i18n.t("uninstall:claudeMdNotFound"));
result.success = true;
}
} catch (error) {
result.errors.push(`Failed to remove CLAUDE.md: ${error.message}`);
}
return result;
}
/**
* 5. Remove permissions and environment variables
*/
async removePermissionsAndEnvs() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const settingsPath = join(homedir(), ".claude", "settings.json");
if (await pathExists(settingsPath)) {
const settings = readJsonConfig(settingsPath) || {};
let modified = false;
if (settings.permissions) {
delete settings.permissions;
result.removedConfigs.push("permissions configuration");
modified = true;
}
if (settings.env) {
delete settings.env;
result.removedConfigs.push("environment variables");
modified = true;
}
if (modified) {
writeJsonConfig(settingsPath, settings);
}
result.success = true;
} else {
result.warnings.push(i18n.t("uninstall:settingsJsonNotFound"));
result.success = true;
}
} catch (error) {
result.errors.push(`Failed to remove permissions and envs: ${error.message}`);
}
return result;
}
/**
* 6. Remove MCP servers from .claude.json (mcpServers field only)
*/
async removeMcps() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const claudeJsonPath = join(homedir(), ".claude.json");
if (await pathExists(claudeJsonPath)) {
const config = readJsonConfig(claudeJsonPath) || {};
if (config.mcpServers) {
delete config.mcpServers;
writeJsonConfig(claudeJsonPath, config);
result.removedConfigs.push("mcpServers from .claude.json");
}
result.success = true;
} else {
result.warnings.push(i18n.t("uninstall:claudeJsonNotFound"));
result.success = true;
}
} catch (error) {
result.errors.push(`Failed to remove MCP servers: ${error.message}`);
}
return result;
}
/**
* 7. Uninstall Claude Code Router and remove configuration
*/
async uninstallCcr() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const ccrPath = join(homedir(), ".claude-code-router");
if (await pathExists(ccrPath)) {
const trashResult = await moveToTrash(ccrPath);
if (!trashResult[0]?.success) {
result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
}
result.removed.push(".claude-code-router/");
}
try {
await exec$1("npm", ["uninstall", "-g", "@musistudio/claude-code-router"]);
result.removed.push("@musistudio/claude-code-router package");
result.success = true;
} catch (npmError) {
if (npmError.message.includes("not found") || npmError.message.includes("not installed")) {
result.warnings.push(i18n.t("uninstall:ccrPackageNotFound"));
result.success = true;
} else {
result.errors.push(`Failed to uninstall CCR package: ${npmError.message}`);
}
}
} catch (error) {
result.errors.push(`Failed to uninstall CCR: ${error.message}`);
}
return result;
}
/**
* 8. Uninstall CCometixLine
*/
async uninstallCcline() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
await exec$1("npm", ["uninstall", "-g", "@cometix/ccline"]);
result.removed.push("@cometix/ccline package");
result.success = true;
} catch (error) {
if (error.message.includes("not found") || error.message.includes("not installed")) {
result.warnings.push(i18n.t("uninstall:cclinePackageNotFound"));
result.success = true;
} else {
result.errors.push(`Failed to uninstall CCometixLine: ${error.message}`);
}
}
return result;
}
/**
* 9. Uninstall Claude Code and remove entire .claude.json
*/
async uninstallClaudeCode() {
const result = {
success: false,
removed: [],
removedConfigs: [],
errors: [],
warnings: []
};
try {
const claudeJsonPath = join(homedir(), ".claude.json");
if (await pathExists(claudeJsonPath)) {
const trashResult = await moveToTrash(claudeJsonPath);
if (!trashResult[0]?.success) {
result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
}
result.removed.push(".claude.json (includes MCP configuration)");
}
try {
await exec$1("npm", ["uninstall", "-g", "@anthropic-ai/claude-code"]);
result.removed.push("@anthropic-ai/claude-code package");
result.success = true;
} catch (npmError) {
if (npmError.message.includes("not found") || npmError.message.includes("not installed")) {
result.warnings.push(i18n.t("uninstall:claudeCodePackageNotFound"));
result.success = true;
} else {
result.errors.push(`Failed to uninstall Claude Code package: ${npmError.message}`);
}
}
} catch (error) {
result.errors.push(`Failed to uninstall Claude Code: ${error.message}`);
}
return result;
}
/**
* 10. Remove backup files
*/
async removeBackups() {
const result = {
suc